style: 代码格式化

pull/21/merge
ouqiang 2017-09-16 17:58:33 +08:00
parent 21028ec6f0
commit f0ff9a88a7
47 changed files with 3411 additions and 3458 deletions

View File

@ -1,194 +1,192 @@
package cmd package cmd
import ( import (
"github.com/ouqiang/gocron/modules/app" "github.com/ouqiang/gocron/models"
"github.com/ouqiang/gocron/routers" "github.com/ouqiang/gocron/modules/app"
"github.com/urfave/cli" "github.com/ouqiang/gocron/modules/logger"
"gopkg.in/macaron.v1" "github.com/ouqiang/gocron/modules/rpc/grpcpool"
"os" "github.com/ouqiang/gocron/modules/setting"
"os/signal" "github.com/ouqiang/gocron/routers"
"syscall" "github.com/ouqiang/gocron/service"
"github.com/ouqiang/gocron/modules/logger" "github.com/urfave/cli"
"github.com/ouqiang/gocron/service" "gopkg.in/macaron.v1"
"github.com/ouqiang/gocron/models" "os"
"github.com/ouqiang/gocron/modules/setting" "os/signal"
"time" "syscall"
"github.com/ouqiang/gocron/modules/rpc/grpcpool" "time"
) )
// web服务器默认端口 // web服务器默认端口
const DefaultPort = 5920 const DefaultPort = 5920
var CmdWeb = cli.Command{ var CmdWeb = cli.Command{
Name: "web", Name: "web",
Usage: "run web server", Usage: "run web server",
Action: runWeb, Action: runWeb,
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.StringFlag{ cli.StringFlag{
Name: "host", Name: "host",
Value: "0.0.0.0", Value: "0.0.0.0",
Usage: "bind host", Usage: "bind host",
}, },
cli.IntFlag{ cli.IntFlag{
Name: "port,p", Name: "port,p",
Value: DefaultPort, Value: DefaultPort,
Usage: "bind port", Usage: "bind port",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "env,e", Name: "env,e",
Value: "prod", Value: "prod",
Usage: "runtime environment, dev|test|prod", Usage: "runtime environment, dev|test|prod",
}, },
}, },
} }
func runWeb(ctx *cli.Context) { func runWeb(ctx *cli.Context) {
// 设置运行环境 // 设置运行环境
setEnvironment(ctx) setEnvironment(ctx)
// 初始化应用 // 初始化应用
app.InitEnv(ctx.App.Version) app.InitEnv(ctx.App.Version)
// 初始化模块 DB、定时任务等 // 初始化模块 DB、定时任务等
initModule() initModule()
// 捕捉信号,配置热更新等 // 捕捉信号,配置热更新等
go catchSignal() go catchSignal()
m := macaron.Classic() m := macaron.Classic()
// 注册路由 // 注册路由
routers.Register(m) routers.Register(m)
// 注册中间件. // 注册中间件.
routers.RegisterMiddleware(m) routers.RegisterMiddleware(m)
host := parseHost(ctx) host := parseHost(ctx)
port := parsePort(ctx) port := parsePort(ctx)
m.Run(host, port) m.Run(host, port)
} }
func initModule() { func initModule() {
if !app.Installed { if !app.Installed {
return return
} }
config, err := setting.Read(app.AppConfig) config, err := setting.Read(app.AppConfig)
if err != nil { if err != nil {
logger.Fatal("读取应用配置失败", err) logger.Fatal("读取应用配置失败", err)
} }
app.Setting = config app.Setting = config
// 初始化DB // 初始化DB
models.Db = models.CreateDb() models.Db = models.CreateDb()
// 版本升级 // 版本升级
upgradeIfNeed() upgradeIfNeed()
// 初始化定时任务 // 初始化定时任务
serviceTask := new(service.Task) serviceTask := new(service.Task)
serviceTask.Initialize() serviceTask.Initialize()
} }
// 解析端口 // 解析端口
func parsePort(ctx *cli.Context) int { func parsePort(ctx *cli.Context) int {
var port int = DefaultPort var port int = DefaultPort
if ctx.IsSet("port") { if ctx.IsSet("port") {
port = ctx.Int("port") port = ctx.Int("port")
} }
if port <= 0 || port >= 65535 { if port <= 0 || port >= 65535 {
port = DefaultPort port = DefaultPort
} }
return port return port
} }
func parseHost(ctx *cli.Context) string { func parseHost(ctx *cli.Context) string {
if ctx.IsSet("host") { if ctx.IsSet("host") {
return ctx.String("host") return ctx.String("host")
} }
return "0.0.0.0" return "0.0.0.0"
} }
func setEnvironment(ctx *cli.Context) { func setEnvironment(ctx *cli.Context) {
var env string = "prod" var env string = "prod"
if ctx.IsSet("env") { if ctx.IsSet("env") {
env = ctx.String("env") env = ctx.String("env")
} }
switch env { switch env {
case "test": case "test":
macaron.Env = macaron.TEST macaron.Env = macaron.TEST
case "dev": case "dev":
macaron.Env = macaron.DEV macaron.Env = macaron.DEV
default: default:
macaron.Env = macaron.PROD macaron.Env = macaron.PROD
} }
} }
// 捕捉信号 // 捕捉信号
func catchSignal() { func catchSignal() {
c := make(chan os.Signal) c := make(chan os.Signal)
// todo 配置热更新, windows 不支持 syscall.SIGUSR1, syscall.SIGUSR2 // todo 配置热更新, windows 不支持 syscall.SIGUSR1, syscall.SIGUSR2
signal.Notify(c, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM) signal.Notify(c, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
for { for {
s := <- c s := <-c
logger.Info("收到信号 -- ", s) logger.Info("收到信号 -- ", s)
switch s { switch s {
case syscall.SIGHUP: case syscall.SIGHUP:
logger.Info("收到终端断开信号, 忽略") logger.Info("收到终端断开信号, 忽略")
case syscall.SIGINT, syscall.SIGTERM: case syscall.SIGINT, syscall.SIGTERM:
shutdown() shutdown()
} }
} }
} }
// 应用退出 // 应用退出
func shutdown() { func shutdown() {
defer func() { defer func() {
logger.Info("已退出") logger.Info("已退出")
os.Exit(0) os.Exit(0)
}() }()
if !app.Installed { if !app.Installed {
return return
} }
logger.Info("应用准备退出") logger.Info("应用准备退出")
serviceTask := new(service.Task) serviceTask := new(service.Task)
// 停止所有任务调度 // 停止所有任务调度
logger.Info("停止定时任务调度") logger.Info("停止定时任务调度")
serviceTask.StopAll() serviceTask.StopAll()
taskNumInRunning := service.TaskNum.Num() taskNumInRunning := service.TaskNum.Num()
logger.Infof("正在运行的任务有%d个", taskNumInRunning) logger.Infof("正在运行的任务有%d个", taskNumInRunning)
if taskNumInRunning > 0 { if taskNumInRunning > 0 {
logger.Info("等待所有任务执行完成后退出") logger.Info("等待所有任务执行完成后退出")
} }
for { for {
if taskNumInRunning <= 0 { if taskNumInRunning <= 0 {
break break
} }
time.Sleep(3 * time.Second) time.Sleep(3 * time.Second)
taskNumInRunning = service.TaskNum.Num() taskNumInRunning = service.TaskNum.Num()
} }
// 释放gRPC连接池 // 释放gRPC连接池
grpcpool.Pool.ReleaseAll() grpcpool.Pool.ReleaseAll()
} }
// 判断应用是否需要升级, 当存在版本号文件且版本小于app.VersionId时升级 // 判断应用是否需要升级, 当存在版本号文件且版本小于app.VersionId时升级
func upgradeIfNeed() { func upgradeIfNeed() {
currentVersionId := app.GetCurrentVersionId() currentVersionId := app.GetCurrentVersionId()
// 没有版本号文件 // 没有版本号文件
if currentVersionId == 0 { if currentVersionId == 0 {
return; return
} }
if currentVersionId >= app.VersionId { if currentVersionId >= app.VersionId {
return return
} }
migration := new(models.Migration) migration := new(models.Migration)
logger.Infof("版本升级开始, 当前版本号%d", currentVersionId) logger.Infof("版本升级开始, 当前版本号%d", currentVersionId)
migration.Upgrade(currentVersionId) migration.Upgrade(currentVersionId)
app.UpdateVersionFile() app.UpdateVersionFile()
logger.Infof("已升级到最新版本%d", app.VersionId) logger.Infof("已升级到最新版本%d", app.VersionId)
} }

View File

@ -4,67 +4,65 @@
package main package main
import ( import (
"flag"
"fmt"
"github.com/ouqiang/gocron/modules/rpc/auth"
"github.com/ouqiang/gocron/modules/rpc/server" "github.com/ouqiang/gocron/modules/rpc/server"
"flag" "github.com/ouqiang/gocron/modules/utils"
"runtime" "os"
"os" "runtime"
"fmt" "strings"
"strings"
"github.com/ouqiang/gocron/modules/rpc/auth"
"github.com/ouqiang/gocron/modules/utils"
) )
const AppVersion = "1.2.2" const AppVersion = "1.2.2"
func main() { func main() {
var serverAddr string var serverAddr string
var allowRoot bool var allowRoot bool
var version bool var version bool
var CAFile string var CAFile string
var certFile string var certFile string
var keyFile string var keyFile string
var enableTLS bool var enableTLS bool
flag.BoolVar(&allowRoot, "allow-root", false, "./gocron-node -allow-root") 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.StringVar(&serverAddr, "s", "0.0.0.0:5921", "./gocron-node -s ip:port")
flag.BoolVar(&version, "v", false, "./gocron-node -v") flag.BoolVar(&version, "v", false, "./gocron-node -v")
flag.BoolVar(&enableTLS, "enable-tls", false, "./gocron-node -enable-tls") flag.BoolVar(&enableTLS, "enable-tls", false, "./gocron-node -enable-tls")
flag.StringVar(&CAFile, "ca-file", "", "./gocron-node -ca-file path") flag.StringVar(&CAFile, "ca-file", "", "./gocron-node -ca-file path")
flag.StringVar(&certFile, "cert-file", "", "./gocron-node -cert-file path") flag.StringVar(&certFile, "cert-file", "", "./gocron-node -cert-file path")
flag.StringVar(&keyFile, "key-file", "", "./gocron-node -key-file path") flag.StringVar(&keyFile, "key-file", "", "./gocron-node -key-file path")
flag.Parse() flag.Parse()
if version { if version {
fmt.Println(AppVersion) fmt.Println(AppVersion)
return return
} }
if (enableTLS) { if enableTLS {
if !utils.FileExist(CAFile) { if !utils.FileExist(CAFile) {
fmt.Printf("failed to read ca cert file: %s", CAFile) fmt.Printf("failed to read ca cert file: %s", CAFile)
return return
} }
if !utils.FileExist(certFile) { if !utils.FileExist(certFile) {
fmt.Printf("failed to read server cert file: %s", certFile) fmt.Printf("failed to read server cert file: %s", certFile)
return return
} }
if !utils.FileExist(keyFile) { if !utils.FileExist(keyFile) {
fmt.Printf("failed to read server key file: %s", keyFile) fmt.Printf("failed to read server key file: %s", keyFile)
return return
} }
} }
certificate := auth.Certificate{ certificate := auth.Certificate{
CAFile: strings.TrimSpace(CAFile), CAFile: strings.TrimSpace(CAFile),
CertFile: strings.TrimSpace(certFile), CertFile: strings.TrimSpace(certFile),
KeyFile: strings.TrimSpace(keyFile), KeyFile: strings.TrimSpace(keyFile),
} }
if runtime.GOOS != "windows" && os.Getuid() == 0 && !allowRoot {
fmt.Println("Do not run gocron-node as root user")
return
}
if runtime.GOOS != "windows" && os.Getuid() == 0 && !allowRoot {
fmt.Println("Do not run gocron-node as root user")
return
}
server.Start(serverAddr, enableTLS, certificate) server.Start(serverAddr, enableTLS, certificate)
} }

View File

@ -4,22 +4,22 @@
package main package main
import ( import (
"github.com/urfave/cli" "github.com/urfave/cli"
"os" "os"
"github.com/ouqiang/gocron/cmd" "github.com/ouqiang/gocron/cmd"
) )
const AppVersion = "1.2.2" const AppVersion = "1.2.2"
func main() { func main() {
app := cli.NewApp() app := cli.NewApp()
app.Name = "gocron" app.Name = "gocron"
app.Usage = "gocron service" app.Usage = "gocron service"
app.Version = AppVersion app.Version = AppVersion
app.Commands = []cli.Command{ app.Commands = []cli.Command{
cmd.CmdWeb, cmd.CmdWeb,
} }
app.Flags = append(app.Flags, []cli.Flag{}...) app.Flags = append(app.Flags, []cli.Flag{}...)
app.Run(os.Args) app.Run(os.Args)
} }

View File

@ -1,95 +1,94 @@
package models package models
import ( import (
"github.com/go-xorm/xorm" "github.com/go-xorm/xorm"
) )
// 主机 // 主机
type Host struct { type Host struct {
Id int16 `xorm:"smallint pk autoincr"` Id int16 `xorm:"smallint pk autoincr"`
Name string `xorm:"varchar(64) notnull"` // 主机名称 Name string `xorm:"varchar(64) notnull"` // 主机名称
Alias string `xorm:"varchar(32) notnull default '' "` // 主机别名 Alias string `xorm:"varchar(32) notnull default '' "` // 主机别名
Port int `xorm:"notnull default 22"` // 主机端口 Port int `xorm:"notnull default 22"` // 主机端口
Remark string `xorm:"varchar(100) notnull default '' "` // 备注 Remark string `xorm:"varchar(100) notnull default '' "` // 备注
BaseModel `xorm:"-"` BaseModel `xorm:"-"`
Selected bool `xorm:"-"` Selected bool `xorm:"-"`
} }
// 新增 // 新增
func (host *Host) Create() (insertId int16, err error) { func (host *Host) Create() (insertId int16, err error) {
_, err = Db.Insert(host) _, err = Db.Insert(host)
if err == nil { if err == nil {
insertId = host.Id insertId = host.Id
} }
return return
} }
func (host *Host) UpdateBean(id int16) (int64, error) { func (host *Host) UpdateBean(id int16) (int64, error) {
return Db.ID(id).Cols("name,alias,port,remark").Update(host) return Db.ID(id).Cols("name,alias,port,remark").Update(host)
} }
// 更新 // 更新
func (host *Host) Update(id int, data CommonMap) (int64, error) { 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) { 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 { 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) { func (host *Host) NameExists(name string, id int16) (bool, error) {
if id == 0 { if id == 0 {
count, err := Db.Where("name = ?", name).Count(host); count, err := Db.Where("name = ?", name).Count(host)
return count > 0, err return count > 0, err
} }
count, err := Db.Where("name = ? AND id != ?", name, id).Count(host); count, err := Db.Where("name = ? AND id != ?", name, id).Count(host)
return count > 0, err return count > 0, err
} }
func (host *Host) List(params CommonMap) ([]Host, error) { func (host *Host) List(params CommonMap) ([]Host, error) {
host.parsePageAndPageSize(params) host.parsePageAndPageSize(params)
list := make([]Host, 0) list := make([]Host, 0)
session := Db.Desc("id") session := Db.Desc("id")
host.parseWhere(session, params) host.parseWhere(session, params)
err := session.Limit(host.PageSize, host.pageLimitOffset()).Find(&list) err := session.Limit(host.PageSize, host.pageLimitOffset()).Find(&list)
return list, err return list, err
} }
func (host *Host) AllList() ([]Host, error) { func (host *Host) AllList() ([]Host, error) {
list := make([]Host, 0) list := make([]Host, 0)
err := Db.Cols("name,port").Desc("id").Find(&list) err := Db.Cols("name,port").Desc("id").Find(&list)
return list, err return list, err
} }
func (host *Host) Total(params CommonMap) (int64, error) { func (host *Host) Total(params CommonMap) (int64, error) {
session := Db.NewSession() session := Db.NewSession()
host.parseWhere(session, params) host.parseWhere(session, params)
return session.Count(host) return session.Count(host)
} }
// 解析where // 解析where
func (host *Host) parseWhere(session *xorm.Session, params CommonMap) { func (host *Host) parseWhere(session *xorm.Session, params CommonMap) {
if len(params) == 0 { if len(params) == 0 {
return return
} }
id, ok := params["Id"] id, ok := params["Id"]
if ok && id.(int) > 0 { if ok && id.(int) > 0 {
session.And("id = ?", id) session.And("id = ?", id)
} }
name, ok := params["Name"] name, ok := params["Name"]
if ok && name.(string) != "" { if ok && name.(string) != "" {
session.And("name = ?", name) session.And("name = ?", name)
} }
} }

View File

@ -1,36 +1,36 @@
package models package models
import ( import (
"time" "time"
) )
// 用户登录日志 // 用户登录日志
type LoginLog struct { type LoginLog struct {
Id int `xorm:"pk autoincr notnull "` Id int `xorm:"pk autoincr notnull "`
Username string `xorm:"varchar(32) notnull"` Username string `xorm:"varchar(32) notnull"`
Ip string `xorm:"varchar(15) not null"` Ip string `xorm:"varchar(15) not null"`
Created time.Time `xorm:"datetime notnull created"` Created time.Time `xorm:"datetime notnull created"`
BaseModel `xorm:"-"` BaseModel `xorm:"-"`
} }
func (log *LoginLog) Create() (insertId int, err error) { func (log *LoginLog) Create() (insertId int, err error) {
_, err = Db.Insert(log) _, err = Db.Insert(log)
if err == nil { if err == nil {
insertId = log.Id insertId = log.Id
} }
return return
} }
func (log *LoginLog) List(params CommonMap) ([]LoginLog, error) { func (log *LoginLog) List(params CommonMap) ([]LoginLog, error) {
log.parsePageAndPageSize(params) log.parsePageAndPageSize(params)
list := make([]LoginLog, 0) list := make([]LoginLog, 0)
err := Db.Desc("id").Limit(log.PageSize, log.pageLimitOffset()).Find(&list) err := Db.Desc("id").Limit(log.PageSize, log.pageLimitOffset()).Find(&list)
return list, err return list, err
} }
func (log *LoginLog) Total() (int64, error) { func (log *LoginLog) Total() (int64, error) {
return Db.Count(log) return Db.Count(log)
} }

View File

@ -1,159 +1,157 @@
package models package models
import ( import (
"errors" "errors"
"fmt" "fmt"
"github.com/ouqiang/gocron/modules/logger" "github.com/go-xorm/xorm"
"github.com/go-xorm/xorm" "github.com/ouqiang/gocron/modules/logger"
"strconv" "strconv"
) )
type Migration struct{} type Migration struct{}
// 首次安装, 创建数据库表 // 首次安装, 创建数据库表
func (migration *Migration) Install(dbName string) error { func (migration *Migration) Install(dbName string) error {
if !isDatabaseExist(dbName) { if !isDatabaseExist(dbName) {
return errors.New("数据库不存在") return errors.New("数据库不存在")
} }
setting := new(Setting) setting := new(Setting)
task := new(Task) task := new(Task)
tables := []interface{}{ tables := []interface{}{
&User{}, task, &TaskLog{}, &Host{}, setting,&LoginLog{},&TaskHost{}, &User{}, task, &TaskLog{}, &Host{}, setting, &LoginLog{}, &TaskHost{},
} }
for _, table := range tables { for _, table := range tables {
exist, err:= Db.IsTableExist(table) exist, err := Db.IsTableExist(table)
if exist { if exist {
return errors.New("数据表已存在") return errors.New("数据表已存在")
} }
if err != nil { if err != nil {
return err return err
} }
err = Db.Sync2(table) err = Db.Sync2(table)
if err != nil { if err != nil {
return err return err
} }
} }
setting.InitBasicField() setting.InitBasicField()
task.CreateTestTask() task.CreateTestTask()
return nil return nil
} }
// 判断数据库是否存在 // 判断数据库是否存在
func isDatabaseExist(name string) bool { 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) { func (migration *Migration) Upgrade(oldVersionId int) {
// v1.2版本不支持升级 // v1.2版本不支持升级
if oldVersionId == 120 { if oldVersionId == 120 {
return return
} }
versionIds := []int{110, 122} versionIds := []int{110, 122}
upgradeFuncs := []func(*xorm.Session) error { upgradeFuncs := []func(*xorm.Session) error{
migration.upgradeFor110, migration.upgradeFor110,
migration.upgradeFor122, migration.upgradeFor122,
} }
startIndex := -1 startIndex := -1
// 从当前版本的下一版本开始升级 // 从当前版本的下一版本开始升级
for i, value := range versionIds { for i, value := range versionIds {
if value > oldVersionId { if value > oldVersionId {
startIndex = i startIndex = i
break; break
} }
} }
if startIndex == -1 { if startIndex == -1 {
return return
} }
length := len(versionIds) length := len(versionIds)
if startIndex >= length { if startIndex >= length {
return return
} }
session := Db.NewSession() session := Db.NewSession()
err := session.Begin() err := session.Begin()
if err != nil { if err != nil {
logger.Fatalf("开启事务失败-%s", err.Error()) logger.Fatalf("开启事务失败-%s", err.Error())
} }
for startIndex < length { for startIndex < length {
err = upgradeFuncs[startIndex](session) err = upgradeFuncs[startIndex](session)
if err == nil { if err == nil {
startIndex++ startIndex++
continue continue
} }
dbErr := session.Rollback() dbErr := session.Rollback()
if dbErr != nil { if dbErr != nil {
logger.Fatalf("事务回滚失败-%s",dbErr.Error()) logger.Fatalf("事务回滚失败-%s", dbErr.Error())
} }
logger.Fatal(err) logger.Fatal(err)
} }
err = session.Commit() err = session.Commit()
if err != nil { if err != nil {
logger.Fatalf("提交事务失败-%s", err.Error()) logger.Fatalf("提交事务失败-%s", err.Error())
} }
} }
// 升级到v1.1版本 // 升级到v1.1版本
func (migration *Migration) upgradeFor110(session *xorm.Session) error { func (migration *Migration) upgradeFor110(session *xorm.Session) error {
logger.Info("开始升级到v1.1") logger.Info("开始升级到v1.1")
// 创建表task_host // 创建表task_host
err := session.Sync2(new(TaskHost)) err := session.Sync2(new(TaskHost))
if err != nil { if err != nil {
return err return err
} }
tableName := TablePrefix + "task" tableName := TablePrefix + "task"
// 把task对应的host_id写入task_host表 // 把task对应的host_id写入task_host表
sql := fmt.Sprintf("SELECT id, host_id FROM %s WHERE host_id > 0", tableName) sql := fmt.Sprintf("SELECT id, host_id FROM %s WHERE host_id > 0", tableName)
results, err := session.Query(sql) results, err := session.Query(sql)
if err != nil { if err != nil {
return err return err
} }
for _, value := range results { for _, value := range results {
taskHostModel := &TaskHost{} taskHostModel := &TaskHost{}
taskId, err := strconv.Atoi(string(value["id"])) taskId, err := strconv.Atoi(string(value["id"]))
if err != nil { if err != nil {
return err return err
} }
hostId, err := strconv.Atoi(string(value["host_id"])) hostId, err := strconv.Atoi(string(value["host_id"]))
if err != nil { if err != nil {
return err return err
} }
taskHostModel.TaskId = taskId taskHostModel.TaskId = taskId
taskHostModel.HostId = int16(hostId) taskHostModel.HostId = int16(hostId)
_, err = session.Insert(taskHostModel) _, err = session.Insert(taskHostModel)
if err != nil { if err != nil {
return err return err
} }
} }
// 删除task表host_id字段
_, err = session.Exec(fmt.Sprintf("ALTER TABLE %s DROP COLUMN host_id", tableName))
// 删除task表host_id字段 logger.Info("已升级到v1.1\n")
_, err = session.Exec(fmt.Sprintf("ALTER TABLE %s DROP COLUMN host_id", tableName))
logger.Info("已升级到v1.1\n") return err
return err
} }
// 升级到1.2.2版本 // 升级到1.2.2版本
func (migration *Migration) upgradeFor122(session *xorm.Session) error { func (migration *Migration) upgradeFor122(session *xorm.Session) error {
logger.Info("开始升级到v1.2.2") logger.Info("开始升级到v1.2.2")
tableName := TablePrefix + "task" tableName := TablePrefix + "task"
// task表增加tag字段 // task表增加tag字段
_, err := session.Exec(fmt.Sprintf("ALTER TABLE %s ADD COLUMN tag VARCHAR(32) NOT NULL DEFAULT '' ", tableName)) _, 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 return err
} }

View File

@ -1,16 +1,16 @@
package models package models
import ( import (
"fmt" "fmt"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"github.com/go-xorm/core" "github.com/go-xorm/core"
"github.com/go-xorm/xorm" "github.com/go-xorm/xorm"
"gopkg.in/macaron.v1" "github.com/ouqiang/gocron/modules/app"
"strings" "github.com/ouqiang/gocron/modules/logger"
"github.com/ouqiang/gocron/modules/logger" "github.com/ouqiang/gocron/modules/setting"
"github.com/ouqiang/gocron/modules/app" "gopkg.in/macaron.v1"
"time" "strings"
"github.com/ouqiang/gocron/modules/setting" "time"
) )
type Status int8 type Status int8
@ -20,105 +20,105 @@ var TablePrefix string = ""
var Db *xorm.Engine var Db *xorm.Engine
const ( const (
Disabled Status = 0 // 禁用 Disabled Status = 0 // 禁用
Failure Status = 0 // 失败 Failure Status = 0 // 失败
Enabled Status = 1 // 启用 Enabled Status = 1 // 启用
Running Status = 1 // 运行中 Running Status = 1 // 运行中
Finish Status = 2 // 完成 Finish Status = 2 // 完成
Cancel Status = 3 // 取消 Cancel Status = 3 // 取消
Waiting Status = 5 // 等待中 Waiting Status = 5 // 等待中
) )
const ( const (
Page = 1 // 当前页数 Page = 1 // 当前页数
PageSize = 20 // 每页多少条数据 PageSize = 20 // 每页多少条数据
MaxPageSize = 100000 // 每次最多取多少条 MaxPageSize = 100000 // 每次最多取多少条
) )
const DefaultTimeFormat = "2006-01-02 15:04:05" const DefaultTimeFormat = "2006-01-02 15:04:05"
type BaseModel struct { type BaseModel struct {
Page int `xorm:"-"` Page int `xorm:"-"`
PageSize int `xorm:"-"` PageSize int `xorm:"-"`
} }
func (model *BaseModel) parsePageAndPageSize(params CommonMap) { func (model *BaseModel) parsePageAndPageSize(params CommonMap) {
page, ok := params["Page"] page, ok := params["Page"]
if ok { if ok {
model.Page = page.(int) model.Page = page.(int)
} }
pageSize, ok := params["PageSize"] pageSize, ok := params["PageSize"]
if ok { if ok {
model.PageSize = pageSize.(int) model.PageSize = pageSize.(int)
} }
if model.Page <= 0 { if model.Page <= 0 {
model.Page = Page model.Page = Page
} }
if model.PageSize <= 0 { if model.PageSize <= 0 {
model.PageSize = MaxPageSize model.PageSize = MaxPageSize
} }
} }
func (model *BaseModel) pageLimitOffset() int { func (model *BaseModel) pageLimitOffset() int {
return (model.Page - 1) * model.PageSize return (model.Page - 1) * model.PageSize
} }
// 创建Db // 创建Db
func CreateDb() *xorm.Engine { func CreateDb() *xorm.Engine {
dsn := getDbEngineDSN(app.Setting) dsn := getDbEngineDSN(app.Setting)
engine, err := xorm.NewEngine(app.Setting.Db.Engine, dsn) engine, err := xorm.NewEngine(app.Setting.Db.Engine, dsn)
if err != nil { if err != nil {
logger.Fatal("创建xorm引擎失败", err) logger.Fatal("创建xorm引擎失败", err)
} }
engine.SetMaxIdleConns(app.Setting.Db.MaxIdleConns) engine.SetMaxIdleConns(app.Setting.Db.MaxIdleConns)
engine.SetMaxOpenConns(app.Setting.Db.MaxOpenConns) engine.SetMaxOpenConns(app.Setting.Db.MaxOpenConns)
if app.Setting.Db.Prefix != "" { if app.Setting.Db.Prefix != "" {
// 设置表前缀 // 设置表前缀
TablePrefix = app.Setting.Db.Prefix TablePrefix = app.Setting.Db.Prefix
mapper := core.NewPrefixMapper(core.SnakeMapper{}, app.Setting.Db.Prefix) mapper := core.NewPrefixMapper(core.SnakeMapper{}, app.Setting.Db.Prefix)
engine.SetTableMapper(mapper) engine.SetTableMapper(mapper)
} }
// 本地环境开启日志 // 本地环境开启日志
if macaron.Env == macaron.DEV { if macaron.Env == macaron.DEV {
engine.ShowSQL(true) engine.ShowSQL(true)
engine.Logger().SetLevel(core.LOG_DEBUG) engine.Logger().SetLevel(core.LOG_DEBUG)
} }
go keepDbAlived(engine) go keepDbAlived(engine)
return engine return engine
} }
// 创建临时数据库连接 // 创建临时数据库连接
func CreateTmpDb(setting *setting.Setting) (*xorm.Engine, error) { func CreateTmpDb(setting *setting.Setting) (*xorm.Engine, error) {
dsn := getDbEngineDSN(setting) dsn := getDbEngineDSN(setting)
return xorm.NewEngine(setting.Db.Engine, dsn) return xorm.NewEngine(setting.Db.Engine, dsn)
} }
// 获取数据库引擎DSN mysql,sqlite // 获取数据库引擎DSN mysql,sqlite
func getDbEngineDSN(setting *setting.Setting) string { func getDbEngineDSN(setting *setting.Setting) string {
engine := strings.ToLower(setting.Db.Engine) engine := strings.ToLower(setting.Db.Engine)
var dsn string = "" var dsn string = ""
switch engine { switch engine {
case "mysql": case "mysql":
dsn = fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s", dsn = fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s",
setting.Db.User, setting.Db.User,
setting.Db.Password, setting.Db.Password,
setting.Db.Host, setting.Db.Host,
setting.Db.Port , setting.Db.Port,
setting.Db.Database, setting.Db.Database,
setting.Db.Charset) setting.Db.Charset)
} }
return dsn return dsn
} }
func keepDbAlived(engine *xorm.Engine) { func keepDbAlived(engine *xorm.Engine) {
t := time.Tick(180 * time.Second) t := time.Tick(180 * time.Second)
for { for {
<- t <-t
engine.Ping() engine.Ping()
} }
} }

View File

@ -1,14 +1,14 @@
package models package models
import ( import (
"encoding/json" "encoding/json"
) )
type Setting struct { type Setting struct {
Id int `xorm:"int pk autoincr"` Id int `xorm:"int pk autoincr"`
Code string `xorm:"varchar(32) notnull"` Code string `xorm:"varchar(32) notnull"`
Key string `xorm:"varchar(64) notnull"` Key string `xorm:"varchar(64) notnull"`
Value string `xorm:"varchar(4096) notnull default '' "` Value string `xorm:"varchar(4096) notnull default '' "`
} }
const SlackCode = "slack" const SlackCode = "slack"
@ -21,154 +21,154 @@ const MailUserKey = "user"
// 初始化基本字段 邮件、slack等 // 初始化基本字段 邮件、slack等
func (setting *Setting) InitBasicField() { func (setting *Setting) InitBasicField() {
setting.Code = SlackCode; setting.Code = SlackCode
setting.Key = SlackUrlKey setting.Key = SlackUrlKey
Db.Insert(setting) Db.Insert(setting)
setting.Id = 0 setting.Id = 0
setting.Code = MailCode setting.Code = MailCode
setting.Key = MailServerKey setting.Key = MailServerKey
Db.Insert(setting) Db.Insert(setting)
} }
// region slack配置 // region slack配置
type Slack struct { type Slack struct {
Url string Url string
Channels []Channel Channels []Channel
} }
type Channel struct { type Channel struct {
Id int Id int
Name string Name string
} }
func (setting *Setting) Slack() (Slack, error) { func (setting *Setting) Slack() (Slack, error) {
list := make([]Setting, 0) list := make([]Setting, 0)
err := Db.Where("code = ?", SlackCode).Find(&list) err := Db.Where("code = ?", SlackCode).Find(&list)
slack := Slack{Url:"", Channels:make([]Channel, 0)} slack := Slack{Url: "", Channels: make([]Channel, 0)}
if err != nil { if err != nil {
return slack, err 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) { func (setting *Setting) formatSlack(list []Setting, slack *Slack) {
for _, v := range list { for _, v := range list {
if v.Key == SlackUrlKey { if v.Key == SlackUrlKey {
slack.Url = v.Value slack.Url = v.Value
continue continue
} }
slack.Channels = append(slack.Channels, Channel{ slack.Channels = append(slack.Channels, Channel{
v.Id, v.Value, v.Id, v.Value,
}) })
} }
} }
// 更新slack webhook url // 更新slack webhook url
func (setting *Setting) UpdateSlackUrl(url string) (int64, error) { 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渠道 // 创建slack渠道
func (setting *Setting) CreateChannel(channel string) (int64, error) { func (setting *Setting) CreateChannel(channel string) (int64, error) {
setting.Code = SlackCode setting.Code = SlackCode
setting.Key = SlackChannelKey setting.Key = SlackChannelKey
setting.Value = channel setting.Value = channel
return Db.Insert(setting) return Db.Insert(setting)
} }
func (setting *Setting) IsChannelExist(channel string) (bool) { func (setting *Setting) IsChannelExist(channel string) bool {
setting.Code = SlackCode setting.Code = SlackCode
setting.Key = SlackChannelKey setting.Key = SlackChannelKey
setting.Value = channel setting.Value = channel
count, _ := Db.Count(setting) count, _ := Db.Count(setting)
return count > 0 return count > 0
} }
// 删除slack渠道 // 删除slack渠道
func (setting *Setting) RemoveChannel(id int) (int64, error) { func (setting *Setting) RemoveChannel(id int) (int64, error) {
setting.Code = SlackCode setting.Code = SlackCode
setting.Key = SlackChannelKey setting.Key = SlackChannelKey
setting.Id = id setting.Id = id
return Db.Delete(setting) return Db.Delete(setting)
} }
// endregion // endregion
type Mail struct { type Mail struct {
Host string Host string
Port int Port int
User string User string
Password string Password string
MailUsers []MailUser MailUsers []MailUser
} }
type MailUser struct { type MailUser struct {
Id int Id int
Username string Username string
Email string Email string
} }
// region 邮件配置 // region 邮件配置
func (setting *Setting) Mail() (Mail, error) { func (setting *Setting) Mail() (Mail, error) {
list := make([]Setting, 0) list := make([]Setting, 0)
err := Db.Where("code = ?", MailCode).Find(&list) err := Db.Where("code = ?", MailCode).Find(&list)
mail := Mail{MailUsers:make([]MailUser, 0)} mail := Mail{MailUsers: make([]MailUser, 0)}
if err != nil { if err != nil {
return mail, err 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) { func (setting *Setting) formatMail(list []Setting, mail *Mail) {
mailUser := MailUser{} mailUser := MailUser{}
for _, v := range list { for _, v := range list {
if v.Key == MailServerKey { if v.Key == MailServerKey {
json.Unmarshal([]byte(v.Value), mail) json.Unmarshal([]byte(v.Value), mail)
continue continue
} }
json.Unmarshal([]byte(v.Value), &mailUser) json.Unmarshal([]byte(v.Value), &mailUser)
mailUser.Id = v.Id mailUser.Id = v.Id
mail.MailUsers = append(mail.MailUsers, mailUser) mail.MailUsers = append(mail.MailUsers, mailUser)
} }
} }
func (setting *Setting) UpdateMailServer(config string) (int64, error) { func (setting *Setting) UpdateMailServer(config string) (int64, error) {
setting.Value = config setting.Value = config
return Db.Cols("value").Update(setting, Setting{Code:MailCode, Key:MailServerKey}) return Db.Cols("value").Update(setting, Setting{Code: MailCode, Key: MailServerKey})
} }
func (setting *Setting) CreateMailUser(username, email string) (int64, error) { func (setting *Setting) CreateMailUser(username, email string) (int64, error) {
setting.Code = MailCode setting.Code = MailCode
setting.Key = MailUserKey setting.Key = MailUserKey
mailUser := MailUser{0, username, email} mailUser := MailUser{0, username, email}
jsonByte, err := json.Marshal(mailUser) jsonByte, err := json.Marshal(mailUser)
if err != nil { if err != nil {
return 0, err return 0, err
} }
setting.Value = string(jsonByte) setting.Value = string(jsonByte)
return Db.Insert(setting) return Db.Insert(setting)
} }
func (setting *Setting) RemoveMailUser(id int) (int64, error) { func (setting *Setting) RemoveMailUser(id int) (int64, error) {
setting.Code = MailCode setting.Code = MailCode
setting.Key = MailUserKey setting.Key = MailUserKey
setting.Id = id setting.Id = id
return Db.Delete(setting) return Db.Delete(setting)
} }
// endregion
// endregion

View File

@ -1,272 +1,271 @@
package models package models
import ( import (
"time" "errors"
"github.com/go-xorm/xorm" "github.com/go-xorm/xorm"
"errors" "strings"
"strings" "time"
) )
type TaskProtocol int8 type TaskProtocol int8
const ( const (
TaskHTTP TaskProtocol = iota + 1 // HTTP协议 TaskHTTP TaskProtocol = iota + 1 // HTTP协议
TaskRPC // RPC方式执行命令 TaskRPC // RPC方式执行命令
) )
type TaskLevel int8 type TaskLevel int8
const ( const (
TaskLevelParent TaskLevel = 1 // 父任务 TaskLevelParent TaskLevel = 1 // 父任务
TaskLevelChild TaskLevel = 2 // 子任务(依赖任务) TaskLevelChild TaskLevel = 2 // 子任务(依赖任务)
) )
type TaskDependencyStatus int8 type TaskDependencyStatus int8
const ( const (
TaskDependencyStatusStrong TaskDependencyStatus = 1 // 强依赖 TaskDependencyStatusStrong TaskDependencyStatus = 1 // 强依赖
TaskDependencyStatusWeak TaskDependencyStatus = 2 // 弱依赖 TaskDependencyStatusWeak TaskDependencyStatus = 2 // 弱依赖
) )
// 任务 // 任务
type Task struct { type Task struct {
Id int `xorm:"int pk autoincr"` Id int `xorm:"int pk autoincr"`
Name string `xorm:"varchar(32) notnull"` // 任务名称 Name string `xorm:"varchar(32) notnull"` // 任务名称
Level TaskLevel `xorm:"smallint notnull index default 1"` // 任务等级 1: 主任务 2: 依赖任务 Level TaskLevel `xorm:"smallint notnull index default 1"` // 任务等级 1: 主任务 2: 依赖任务
DependencyTaskId string `xorm:"varchar(64) notnull default ''"` // 依赖任务ID,多个ID逗号分隔 DependencyTaskId string `xorm:"varchar(64) notnull default ''"` // 依赖任务ID,多个ID逗号分隔
DependencyStatus TaskDependencyStatus `xorm:"smallint notnull default 1"` // 依赖关系 1:强依赖 主任务执行成功, 依赖任务才会被执行 2:弱依赖 DependencyStatus TaskDependencyStatus `xorm:"smallint notnull default 1"` // 依赖关系 1:强依赖 主任务执行成功, 依赖任务才会被执行 2:弱依赖
Spec string `xorm:"varchar(64) notnull"` // crontab Spec string `xorm:"varchar(64) notnull"` // crontab
Protocol TaskProtocol `xorm:"tinyint notnull index"` // 协议 1:http 2:系统命令 Protocol TaskProtocol `xorm:"tinyint notnull index"` // 协议 1:http 2:系统命令
Command string `xorm:"varchar(256) notnull"` // URL地址或shell命令 Command string `xorm:"varchar(256) notnull"` // URL地址或shell命令
Timeout int `xorm:"mediumint notnull default 0"` // 任务执行超时时间(单位秒),0不限制 Timeout int `xorm:"mediumint notnull default 0"` // 任务执行超时时间(单位秒),0不限制
Multi int8 `xorm:"tinyint notnull default 1"` // 是否允许多实例运行 Multi int8 `xorm:"tinyint notnull default 1"` // 是否允许多实例运行
RetryTimes int8 `xorm:"tinyint notnull default 0"` // 重试次数 RetryTimes int8 `xorm:"tinyint notnull default 0"` // 重试次数
NotifyStatus int8 `xorm:"smallint notnull default 1"` // 任务执行结束是否通知 0: 不通知 1: 失败通知 2: 执行结束通知 NotifyStatus int8 `xorm:"smallint notnull default 1"` // 任务执行结束是否通知 0: 不通知 1: 失败通知 2: 执行结束通知
NotifyType int8 `xorm:"smallint notnull default 0"` // 通知类型 1: 邮件 2: slack NotifyType int8 `xorm:"smallint notnull default 0"` // 通知类型 1: 邮件 2: slack
NotifyReceiverId string `xorm:"varchar(256) notnull default '' "` // 通知接受者ID, setting表主键ID多个ID逗号分隔 NotifyReceiverId string `xorm:"varchar(256) notnull default '' "` // 通知接受者ID, setting表主键ID多个ID逗号分隔
Tag string `xorm:"varchar(32) notnull default ''"` Tag string `xorm:"varchar(32) notnull default ''"`
Remark string `xorm:"varchar(100) notnull default ''"` // 备注 Remark string `xorm:"varchar(100) notnull default ''"` // 备注
Status Status `xorm:"tinyint notnull index default 0"` // 状态 1:正常 0:停止 Status Status `xorm:"tinyint notnull index default 0"` // 状态 1:正常 0:停止
Created time.Time `xorm:"datetime notnull created"` // 创建时间 Created time.Time `xorm:"datetime notnull created"` // 创建时间
Deleted time.Time `xorm:"datetime deleted"` // 删除时间 Deleted time.Time `xorm:"datetime deleted"` // 删除时间
BaseModel `xorm:"-"` BaseModel `xorm:"-"`
Hosts []TaskHostDetail `xorm:"-"` Hosts []TaskHostDetail `xorm:"-"`
} }
func taskHostTableName() []string { func taskHostTableName() []string {
return []string{TablePrefix + "task_host", "th"} return []string{TablePrefix + "task_host", "th"}
} }
// 新增 // 新增
func (task *Task) Create() (insertId int, err error) { func (task *Task) Create() (insertId int, err error) {
_, err = Db.Insert(task) _, err = Db.Insert(task)
if err == nil { if err == nil {
insertId = task.Id insertId = task.Id
} }
return return
} }
// 新增测试任务 // 新增测试任务
func (task *Task) CreateTestTask() { func (task *Task) CreateTestTask() {
// HTTP任务 // HTTP任务
task.Name = "测试HTTP任务" task.Name = "测试HTTP任务"
task.Level = TaskLevelParent task.Level = TaskLevelParent
task.Protocol = TaskHTTP task.Protocol = TaskHTTP
task.Spec = "*/30 * * * * *" task.Spec = "*/30 * * * * *"
task.Tag = "test-task" task.Tag = "test-task"
// 查询IP地址区域信息 // 查询IP地址区域信息
task.Command = "http://ip.taobao.com/service/getIpInfo.php?ip=117.27.140.253" task.Command = "http://ip.taobao.com/service/getIpInfo.php?ip=117.27.140.253"
task.Status = Enabled task.Status = Enabled
task.Create() task.Create()
} }
func (task *Task) UpdateBean(id int) (int64, error) { func (task *Task) UpdateBean(id int) (int64, error) {
return Db.ID(id). 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"). 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) Update(task)
} }
// 更新 // 更新
func (task *Task) Update(id int, data CommonMap) (int64, error) { 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) { 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) { 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) { 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) { func (task *Task) ActiveList() ([]Task, error) {
list := make([]Task, 0) list := make([]Task, 0)
err := Db.Where("status = ? AND level = ?", Enabled, TaskLevelParent). err := Db.Where("status = ? AND level = ?", Enabled, TaskLevelParent).
Find(&list) Find(&list)
if err != nil { if err != nil {
return list, err return list, err
} }
return task.setHostsForTasks(list) return task.setHostsForTasks(list)
} }
// 获取某个主机下的所有激活任务 // 获取某个主机下的所有激活任务
func (task *Task) ActiveListByHostId(hostId int16) ([]Task, error) { func (task *Task) ActiveListByHostId(hostId int16) ([]Task, error) {
taskHostModel := new(TaskHost) taskHostModel := new(TaskHost)
taskIds, err := taskHostModel.GetTaskIdsByHostId(hostId) taskIds, err := taskHostModel.GetTaskIdsByHostId(hostId)
if err != nil { if err != nil {
return nil, err return nil, err
} }
list := make([]Task, 0) list := make([]Task, 0)
err = Db.Where("status = ? AND level = ?", Enabled, TaskLevelParent). err = Db.Where("status = ? AND level = ?", Enabled, TaskLevelParent).
In("id", taskIds...). In("id", taskIds...).
Find(&list) Find(&list)
if err != nil { if err != nil {
return list, err return list, err
} }
return task.setHostsForTasks(list) return task.setHostsForTasks(list)
} }
func (task *Task) setHostsForTasks(tasks []Task) ([]Task, error) { func (task *Task) setHostsForTasks(tasks []Task) ([]Task, error) {
taskHostModel := new(TaskHost) taskHostModel := new(TaskHost)
var err error var err error
for i, value := range tasks { for i, value := range tasks {
taskHostDetails, err := taskHostModel.GetHostIdsByTaskId(value.Id) taskHostDetails, err := taskHostModel.GetHostIdsByTaskId(value.Id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
tasks[i].Hosts = taskHostDetails tasks[i].Hosts = taskHostDetails
} }
return tasks, err return tasks, err
} }
// 判断任务名称是否存在 // 判断任务名称是否存在
func (task *Task) NameExist(name string, id int) (bool, error) { func (task *Task) NameExist(name string, id int) (bool, error) {
if id > 0 { if id > 0 {
count, err := Db.Where("name = ? AND status = ? AND id != ?", name, Enabled, id).Count(task); count, err := Db.Where("name = ? AND status = ? AND id != ?", name, Enabled, id).Count(task)
return count > 0, err return count > 0, err
} }
count, err := Db.Where("name = ? AND status = ?", name, Enabled).Count(task); 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) { func (task *Task) GetStatus(id int) (Status, error) {
exist, err := Db.Id(id).Get(task) exist, err := Db.Id(id).Get(task)
if err != nil { if err != nil {
return 0, err return 0, err
} }
if !exist { if !exist {
return 0, errors.New("not exist") return 0, errors.New("not exist")
} }
return task.Status, nil return task.Status, nil
} }
func(task *Task) Detail(id int) (Task, error) { func (task *Task) Detail(id int) (Task, error) {
t := Task{} t := Task{}
_, err := Db.Where("id=?", id).Get(&t) _, err := Db.Where("id=?", id).Get(&t)
if err != nil { if err != nil {
return t, err return t, err
} }
taskHostModel := new(TaskHost) taskHostModel := new(TaskHost)
t.Hosts, err = taskHostModel.GetHostIdsByTaskId(id) t.Hosts, err = taskHostModel.GetHostIdsByTaskId(id)
return t, err return t, err
} }
func (task *Task) List(params CommonMap) ([]Task, error) { func (task *Task) List(params CommonMap) ([]Task, error) {
task.parsePageAndPageSize(params) task.parsePageAndPageSize(params)
list := make([]Task, 0) list := make([]Task, 0)
session := Db.Alias("t").Join("LEFT", taskHostTableName(), "t.id = th.task_id") session := Db.Alias("t").Join("LEFT", taskHostTableName(), "t.id = th.task_id")
task.parseWhere(session, params) task.parseWhere(session, params)
err := session.GroupBy("t.id").Desc("t.id").Cols("t.*").Limit(task.PageSize, task.pageLimitOffset()).Find(&list) err := session.GroupBy("t.id").Desc("t.id").Cols("t.*").Limit(task.PageSize, task.pageLimitOffset()).Find(&list)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return task.setHostsForTasks(list) return task.setHostsForTasks(list)
} }
// 获取依赖任务列表 // 获取依赖任务列表
func (task *Task) GetDependencyTaskList(ids string) ([]Task, error) { func (task *Task) GetDependencyTaskList(ids string) ([]Task, error) {
list := make([]Task, 0) list := make([]Task, 0)
if ids == "" { if ids == "" {
return list, nil return list, nil
} }
idList := strings.Split(ids, ",") idList := strings.Split(ids, ",")
taskIds := make([]interface{}, len(idList)) taskIds := make([]interface{}, len(idList))
for i, v := range idList { for i, v := range idList {
taskIds[i] = v taskIds[i] = v
} }
fields := "t.*" fields := "t.*"
err := Db.Alias("t"). err := Db.Alias("t").
Where("t.level = ?", TaskLevelChild). Where("t.level = ?", TaskLevelChild).
In("t.id", taskIds). In("t.id", taskIds).
Cols(fields). Cols(fields).
Find(&list) Find(&list)
if err != nil { if err != nil {
return list, err return list, err
} }
return task.setHostsForTasks(list) return task.setHostsForTasks(list)
} }
func (task *Task) Total(params CommonMap) (int64, error) { func (task *Task) Total(params CommonMap) (int64, error) {
session := Db.Alias("t").Join("LEFT", taskHostTableName(), "t.id = th.task_id") session := Db.Alias("t").Join("LEFT", taskHostTableName(), "t.id = th.task_id")
task.parseWhere(session, params) task.parseWhere(session, params)
list := make([]Task, 0) 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 // 解析where
func (task *Task) parseWhere(session *xorm.Session, params CommonMap) { func (task *Task) parseWhere(session *xorm.Session, params CommonMap) {
if len(params) == 0 { if len(params) == 0 {
return return
} }
id, ok := params["Id"] id, ok := params["Id"]
if ok && id.(int) > 0 { if ok && id.(int) > 0 {
session.And("t.id = ?", id) session.And("t.id = ?", id)
} }
hostId, ok := params["HostId"] hostId, ok := params["HostId"]
if ok && hostId.(int) > 0 { if ok && hostId.(int) > 0 {
session.And("th.host_id = ?", hostId) session.And("th.host_id = ?", hostId)
} }
name, ok := params["Name"] name, ok := params["Name"]
if ok && name.(string) != "" { if ok && name.(string) != "" {
session.And("t.name LIKE ?", "%" + name.(string) + "%") session.And("t.name LIKE ?", "%"+name.(string)+"%")
} }
protocol, ok := params["Protocol"] protocol, ok := params["Protocol"]
if ok && protocol.(int) > 0 { if ok && protocol.(int) > 0 {
session.And("protocol = ?", protocol) session.And("protocol = ?", protocol)
} }
status, ok := params["Status"] status, ok := params["Status"]
if ok && status.(int) > -1 { if ok && status.(int) > -1 {
session.And("status = ?", status) session.And("status = ?", status)
} }
tag, ok := params["Tag"] tag, ok := params["Tag"]
if ok && tag.(string) != "" { if ok && tag.(string) != "" {
session.And("tag = ? ", tag) session.And("tag = ? ", tag)
} }
} }

View File

@ -1,81 +1,80 @@
package models package models
type TaskHost struct { type TaskHost struct {
Id int `xorm:"int pk autoincr"` Id int `xorm:"int pk autoincr"`
TaskId int `xorm:"int not null index"` TaskId int `xorm:"int not null index"`
HostId int16 `xorm:"smallint not null index"` HostId int16 `xorm:"smallint not null index"`
} }
type TaskHostDetail struct { type TaskHostDetail struct {
TaskHost `xorm:"extends"` TaskHost `xorm:"extends"`
Name string Name string
Port int Port int
Alias string Alias string
} }
func (TaskHostDetail) TableName() string { func (TaskHostDetail) TableName() string {
return TablePrefix + "task_host" return TablePrefix + "task_host"
} }
func hostTableName() []string { func hostTableName() []string {
return []string{TablePrefix + "host", "h"} return []string{TablePrefix + "host", "h"}
} }
func (th *TaskHost) Remove(taskId int) error { 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 { func (th *TaskHost) Add(taskId int, hostIds []int) error {
err := th.Remove(taskId) err := th.Remove(taskId)
if err != nil { if err != nil {
return err return err
} }
taskHosts := make([]TaskHost, len(hostIds)) taskHosts := make([]TaskHost, len(hostIds))
for i, value := range hostIds { for i, value := range hostIds {
taskHosts[i].TaskId = taskId taskHosts[i].TaskId = taskId
taskHosts[i].HostId = int16(value) 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) { func (th *TaskHost) GetHostIdsByTaskId(taskId int) ([]TaskHostDetail, error) {
list := make([]TaskHostDetail, 0) list := make([]TaskHostDetail, 0)
fields := "th.id,th.host_id,h.alias,h.name,h.port" fields := "th.id,th.host_id,h.alias,h.name,h.port"
err := Db.Alias("th"). err := Db.Alias("th").
Join("LEFT", hostTableName(), "th.host_id=h.id"). Join("LEFT", hostTableName(), "th.host_id=h.id").
Where("th.task_id = ?", taskId). Where("th.task_id = ?", taskId).
Cols(fields). Cols(fields).
Find(&list) Find(&list)
return list, err return list, err
} }
func (th *TaskHost) GetTaskIdsByHostId(hostId int16) ([]interface{}, error) { func (th *TaskHost) GetTaskIdsByHostId(hostId int16) ([]interface{}, error) {
list := make([]TaskHost, 0) list := make([]TaskHost, 0)
err := Db.Where("host_id = ?", hostId).Cols("task_id").Find(&list) err := Db.Where("host_id = ?", hostId).Cols("task_id").Find(&list)
if err != nil { if err != nil {
return nil, err return nil, err
} }
taskIds := make([]interface{}, len(list)) taskIds := make([]interface{}, len(list))
for i, value := range list { for i, value := range list {
taskIds[i] = value.TaskId taskIds[i] = value.TaskId
} }
return taskIds, err return taskIds, err
} }
// 判断主机id是否有引用 // 判断主机id是否有引用
func (th *TaskHost) HostIdExist(hostId int16) (bool, error) { 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 return count > 0, err
} }

View File

@ -1,99 +1,98 @@
package models package models
import ( import (
"time" "github.com/go-xorm/xorm"
"github.com/go-xorm/xorm" "time"
) )
type TaskType int8 type TaskType int8
// 任务执行日志 // 任务执行日志
type TaskLog struct { type TaskLog struct {
Id int64 `xorm:"bigint pk autoincr"` Id int64 `xorm:"bigint pk autoincr"`
TaskId int `xorm:"int notnull index default 0"` // 任务id TaskId int `xorm:"int notnull index default 0"` // 任务id
Name string `xorm:"varchar(32) notnull"` // 任务名称 Name string `xorm:"varchar(32) notnull"` // 任务名称
Spec string `xorm:"varchar(64) notnull"` // crontab Spec string `xorm:"varchar(64) notnull"` // crontab
Protocol TaskProtocol `xorm:"tinyint notnull index"` // 协议 1:http 2:RPC Protocol TaskProtocol `xorm:"tinyint notnull index"` // 协议 1:http 2:RPC
Command string `xorm:"varchar(256) notnull"` // URL地址或shell命令 Command string `xorm:"varchar(256) notnull"` // URL地址或shell命令
Timeout int `xorm:"mediumint notnull default 0"` // 任务执行超时时间(单位秒),0不限制 Timeout int `xorm:"mediumint notnull default 0"` // 任务执行超时时间(单位秒),0不限制
RetryTimes int8 `xorm:"tinyint notnull default 0"` // 任务重试次数 RetryTimes int8 `xorm:"tinyint notnull default 0"` // 任务重试次数
Hostname string `xorm:"varchar(128) notnull defalut '' "` // RPC主机名逗号分隔 Hostname string `xorm:"varchar(128) notnull defalut '' "` // RPC主机名逗号分隔
StartTime time.Time `xorm:"datetime created"` // 开始执行时间 StartTime time.Time `xorm:"datetime created"` // 开始执行时间
EndTime time.Time `xorm:"datetime updated"` // 执行完成(失败)时间 EndTime time.Time `xorm:"datetime updated"` // 执行完成(失败)时间
Status Status `xorm:"tinyint notnull index default 1"` // 状态 0:执行失败 1:执行中 2:执行完毕 3:任务取消(上次任务未执行完成) 4:异步执行 Status Status `xorm:"tinyint notnull index default 1"` // 状态 0:执行失败 1:执行中 2:执行完毕 3:任务取消(上次任务未执行完成) 4:异步执行
Result string `xorm:"mediumtext notnull defalut '' "` // 执行结果 Result string `xorm:"mediumtext notnull defalut '' "` // 执行结果
TotalTime int `xorm:"-"` // 执行总时长 TotalTime int `xorm:"-"` // 执行总时长
BaseModel `xorm:"-"` BaseModel `xorm:"-"`
} }
func (taskLog *TaskLog) Create() (insertId int64, err error) { func (taskLog *TaskLog) Create() (insertId int64, err error) {
_, err = Db.Insert(taskLog) _, err = Db.Insert(taskLog)
if err == nil { if err == nil {
insertId = taskLog.Id insertId = taskLog.Id
} }
return return
} }
// 更新 // 更新
func (taskLog *TaskLog) Update(id int64, data CommonMap) (int64, error) { 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) { func (taskLog *TaskLog) List(params CommonMap) ([]TaskLog, error) {
taskLog.parsePageAndPageSize(params) taskLog.parsePageAndPageSize(params)
list := make([]TaskLog, 0) list := make([]TaskLog, 0)
session := Db.Desc("id") session := Db.Desc("id")
taskLog.parseWhere(session, params) taskLog.parseWhere(session, params)
err := session.Limit(taskLog.PageSize, taskLog.pageLimitOffset()).Find(&list) err := session.Limit(taskLog.PageSize, taskLog.pageLimitOffset()).Find(&list)
if len(list) > 0 { if len(list) > 0 {
for i, item := range list { for i, item := range list {
endTime := item.EndTime endTime := item.EndTime
if item.Status == Running { if item.Status == Running {
endTime = time.Now() endTime = time.Now()
} }
execSeconds := endTime.Sub(item.StartTime).Seconds() execSeconds := endTime.Sub(item.StartTime).Seconds()
list[i].TotalTime = int(execSeconds) list[i].TotalTime = int(execSeconds)
} }
} }
return list, err return list, err
} }
// 清空表 // 清空表
func (taskLog *TaskLog) Clear() (int64, error) { func (taskLog *TaskLog) Clear() (int64, error) {
return Db.Where("1=1").Delete(taskLog); return Db.Where("1=1").Delete(taskLog)
} }
// 删除N个月前的日志 // 删除N个月前的日志
func (taskLog *TaskLog) Remove(id int) (int64, error) { func (taskLog *TaskLog) Remove(id int) (int64, error) {
t := time.Now().AddDate(0, -id, 0) t := time.Now().AddDate(0, -id, 0)
return Db.Where("start_time <= ?", t.Format(DefaultTimeFormat)).Delete(taskLog) return Db.Where("start_time <= ?", t.Format(DefaultTimeFormat)).Delete(taskLog)
} }
func (taskLog *TaskLog) Total(params CommonMap) (int64, error) { func (taskLog *TaskLog) Total(params CommonMap) (int64, error) {
session := Db.NewSession() session := Db.NewSession()
defer session.Close() defer session.Close()
taskLog.parseWhere(session, params) taskLog.parseWhere(session, params)
return session.Count(taskLog) return session.Count(taskLog)
} }
// 解析where // 解析where
func (taskLog *TaskLog) parseWhere(session *xorm.Session, params CommonMap) { func (taskLog *TaskLog) parseWhere(session *xorm.Session, params CommonMap) {
if len(params) == 0 { if len(params) == 0 {
return return
} }
taskId, ok := params["TaskId"] taskId, ok := params["TaskId"]
if ok && taskId.(int) > 0 { if ok && taskId.(int) > 0 {
session.And("task_id = ?", taskId) session.And("task_id = ?", taskId)
} }
protocol, ok := params["Protocol"] protocol, ok := params["Protocol"]
if ok && protocol.(int) > 0 { if ok && protocol.(int) > 0 {
session.And("protocol = ?", protocol) session.And("protocol = ?", protocol)
} }
status, ok := params["Status"] status, ok := params["Status"]
if ok && status.(int) > -1 { if ok && status.(int) > -1 {
session.And("status = ?", status) session.And("status = ?", status)
} }
} }

View File

@ -1,110 +1,110 @@
package models package models
import ( import (
"github.com/ouqiang/gocron/modules/utils" "github.com/ouqiang/gocron/modules/utils"
"time" "time"
) )
const PasswordSaltLength = 6 const PasswordSaltLength = 6
// 用户model // 用户model
type User struct { type User struct {
Id int `xorm:"pk autoincr notnull "` Id int `xorm:"pk autoincr notnull "`
Name string `xorm:"varchar(32) notnull unique"` // 用户名 Name string `xorm:"varchar(32) notnull unique"` // 用户名
Password string `xorm:"char(32) notnull "` // 密码 Password string `xorm:"char(32) notnull "` // 密码
Salt string `xorm:"char(6) notnull "` // 密码盐值 Salt string `xorm:"char(6) notnull "` // 密码盐值
Email string `xorm:"varchar(50) notnull unique default '' "` // 邮箱 Email string `xorm:"varchar(50) notnull unique default '' "` // 邮箱
Created time.Time `xorm:"datetime notnull created"` Created time.Time `xorm:"datetime notnull created"`
Updated time.Time `xorm:"datetime updated"` Updated time.Time `xorm:"datetime updated"`
Deleted time.Time `xorm:"datetime deleted"` Deleted time.Time `xorm:"datetime deleted"`
IsAdmin int8 `xorm:"tinyint notnull default 0"` // 是否是管理员 1:管理员 0:普通用户 IsAdmin int8 `xorm:"tinyint notnull default 0"` // 是否是管理员 1:管理员 0:普通用户
Status Status `xorm:"tinyint notnull default 1"` // 1: 正常 0:禁用 Status Status `xorm:"tinyint notnull default 1"` // 1: 正常 0:禁用
BaseModel `xorm:"-"` BaseModel `xorm:"-"`
} }
// 新增 // 新增
func (user *User) Create() (insertId int, err error) { func (user *User) Create() (insertId int, err error) {
user.Status = Enabled user.Status = Enabled
user.Salt = user.generateSalt() user.Salt = user.generateSalt()
user.Password = user.encryptPassword(user.Password, user.Salt) user.Password = user.encryptPassword(user.Password, user.Salt)
_, err = Db.Insert(user) _, err = Db.Insert(user)
if err == nil { if err == nil {
insertId = user.Id insertId = user.Id
} }
return return
} }
// 更新 // 更新
func (user *User) Update(id int, data CommonMap) (int64, error) { 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) { func (user *User) UpdatePassword(id int, password string) (int64, error) {
salt := user.generateSalt() salt := user.generateSalt()
safePassword := user.encryptPassword(password, salt) 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) { 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) { 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) { 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 { func (user *User) Match(username, password string) bool {
where := "(name = ? OR email = ?)" where := "(name = ? OR email = ?)"
_, err := Db.Where(where, username, username).Get(user) _, err := Db.Where(where, username, username).Get(user)
if err != nil { if err != nil {
return false return false
} }
hashPassword := user.encryptPassword(password, user.Salt) hashPassword := user.encryptPassword(password, user.Salt)
if hashPassword != user.Password { if hashPassword != user.Password {
return false return false
} }
return true return true
} }
// 用户名是否存在 // 用户名是否存在
func (user *User) UsernameExists(username string) (int64, error) { 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) { 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) { func (user *User) List() ([]User, error) {
list := make([]User, 0) list := make([]User, 0)
err := Db.Desc("id").Find(&list) err := Db.Desc("id").Find(&list)
return list, err return list, err
} }
func (user *User) Total() (int64, error) { func (user *User) Total() (int64, error) {
return Db.Count(user) return Db.Count(user)
} }
// 密码加密 // 密码加密
func (user *User) encryptPassword(password, salt string) string { func (user *User) encryptPassword(password, salt string) string {
return utils.Md5(password + salt) return utils.Md5(password + salt)
} }
// 生成密码盐值 // 生成密码盐值
func (user *User) generateSalt() string { func (user *User) generateSalt() string {
return utils.RandString(PasswordSaltLength) return utils.RandString(PasswordSaltLength)
} }

View File

@ -1,117 +1,116 @@
package app package app
import ( import (
"os" "os"
"github.com/ouqiang/gocron/modules/logger" "github.com/ouqiang/gocron/modules/logger"
"github.com/ouqiang/gocron/modules/utils" "github.com/ouqiang/gocron/modules/setting"
"io/ioutil" "github.com/ouqiang/gocron/modules/utils"
"strconv" "io/ioutil"
"strings" "strconv"
"github.com/ouqiang/gocron/modules/setting" "strings"
) )
var ( var (
AppDir string // 应用根目录 AppDir string // 应用根目录
ConfDir string // 配置目录 ConfDir string // 配置目录
LogDir string // 日志目录 LogDir string // 日志目录
DataDir string // 存放session等 DataDir string // 存放session等
AppConfig string // 应用配置文件 AppConfig string // 应用配置文件
Installed bool // 应用是否安装过 Installed bool // 应用是否安装过
Setting *setting.Setting // 应用配置 Setting *setting.Setting // 应用配置
VersionId int // 版本号 VersionId int // 版本号
VersionFile string // 版本号文件 VersionFile string // 版本号文件
) )
func InitEnv(versionString string) { func InitEnv(versionString string) {
logger.InitLogger() logger.InitLogger()
wd, err := os.Getwd() wd, err := os.Getwd()
if err != nil { if err != nil {
logger.Fatal(err) logger.Fatal(err)
} }
AppDir = wd AppDir = wd
ConfDir = AppDir + "/conf" ConfDir = AppDir + "/conf"
LogDir = AppDir + "/log" LogDir = AppDir + "/log"
DataDir = AppDir + "/data" DataDir = AppDir + "/data"
AppConfig = ConfDir + "/app.ini" AppConfig = ConfDir + "/app.ini"
VersionFile = ConfDir + "/.version" VersionFile = ConfDir + "/.version"
checkDirExists(ConfDir, LogDir, DataDir) checkDirExists(ConfDir, LogDir, DataDir)
Installed = IsInstalled() Installed = IsInstalled()
VersionId = ToNumberVersion(versionString) VersionId = ToNumberVersion(versionString)
} }
// 判断应用是否已安装 // 判断应用是否已安装
func IsInstalled() bool { func IsInstalled() bool {
_, err := os.Stat(ConfDir + "/install.lock") _, err := os.Stat(ConfDir + "/install.lock")
if os.IsNotExist(err) { if os.IsNotExist(err) {
return false return false
} }
return true return true
} }
// 创建安装锁文件 // 创建安装锁文件
func CreateInstallLock() error { func CreateInstallLock() error {
_, err := os.Create(ConfDir + "/install.lock") _, err := os.Create(ConfDir + "/install.lock")
if err != nil { if err != nil {
logger.Error("创建安装锁文件conf/install.lock失败") logger.Error("创建安装锁文件conf/install.lock失败")
} }
return err return err
} }
// 更新应用版本号文件 // 更新应用版本号文件
func UpdateVersionFile() { func UpdateVersionFile() {
err := ioutil.WriteFile(VersionFile, err := ioutil.WriteFile(VersionFile,
[]byte(strconv.Itoa(VersionId)), []byte(strconv.Itoa(VersionId)),
0644, 0644,
) )
if err != nil { if err != nil {
logger.Fatal(err) logger.Fatal(err)
} }
} }
// 获取应用当前版本号, 从版本号文件中读取 // 获取应用当前版本号, 从版本号文件中读取
func GetCurrentVersionId() int { func GetCurrentVersionId() int {
if !utils.FileExist(VersionFile) { if !utils.FileExist(VersionFile) {
return 0; return 0
} }
bytes, err := ioutil.ReadFile(VersionFile) bytes, err := ioutil.ReadFile(VersionFile)
if err != nil { if err != nil {
logger.Fatal(err) logger.Fatal(err)
} }
versionId, err := strconv.Atoi(strings.TrimSpace(string(bytes))) versionId, err := strconv.Atoi(strings.TrimSpace(string(bytes)))
if err != nil { if err != nil {
logger.Fatal(err) logger.Fatal(err)
} }
return versionId return versionId
} }
// 把字符串版本号a.b.c转换为整数版本号abc // 把字符串版本号a.b.c转换为整数版本号abc
func ToNumberVersion(versionString string) int { func ToNumberVersion(versionString string) int {
v := strings.Replace(versionString, ".", "", -1) v := strings.Replace(versionString, ".", "", -1)
if len(v) < 3 { if len(v) < 3 {
v += "0" v += "0"
} }
versionId, err := strconv.Atoi(v) versionId, err := strconv.Atoi(v)
if err != nil { if err != nil {
logger.Fatal(err) logger.Fatal(err)
} }
return versionId return versionId
} }
// 检测目录是否存在 // 检测目录是否存在
func checkDirExists(path ...string) { func checkDirExists(path ...string) {
for _, value := range path { for _, value := range path {
if !utils.FileExist(value) { if !utils.FileExist(value) {
logger.Fatal(value + "目录不存在或无权限访问") logger.Fatal(value + "目录不存在或无权限访问")
} }
} }
} }

View File

@ -3,81 +3,81 @@ package httpclient
// http-client // http-client
import ( import (
"io/ioutil" "bytes"
"net/http" "fmt"
"time" "io/ioutil"
"fmt" "net/http"
"bytes" "time"
) )
type ResponseWrapper struct { type ResponseWrapper struct {
StatusCode int StatusCode int
Body string Body string
Header http.Header Header http.Header
} }
func Get(url string, timeout int) ResponseWrapper { func Get(url string, timeout int) ResponseWrapper {
req, err := http.NewRequest("GET", url, nil) req, err := http.NewRequest("GET", url, nil)
if err != nil { if err != nil {
return createRequestError(err) return createRequestError(err)
} }
return request(req, timeout) return request(req, timeout)
} }
func PostParams(url string,params string, timeout int) ResponseWrapper { func PostParams(url string, params string, timeout int) ResponseWrapper {
buf := bytes.NewBufferString(params) buf := bytes.NewBufferString(params)
req, err := http.NewRequest("POST", url, buf) req, err := http.NewRequest("POST", url, buf)
if err != nil { if err != nil {
return createRequestError(err) return createRequestError(err)
} }
req.Header.Set("Content-type", "application/x-www-form-urlencoded") 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 { func PostJson(url string, body string, timeout int) ResponseWrapper {
buf := bytes.NewBufferString(body) buf := bytes.NewBufferString(body)
req, err := http.NewRequest("POST", url, buf) req, err := http.NewRequest("POST", url, buf)
if err != nil { if err != nil {
return createRequestError(err) return createRequestError(err)
} }
req.Header.Set("Content-type", "application/json") req.Header.Set("Content-type", "application/json")
return request(req, timeout) return request(req, timeout)
} }
func request(req *http.Request, timeout int) ResponseWrapper { func request(req *http.Request, timeout int) ResponseWrapper {
wrapper := ResponseWrapper{StatusCode: 0, Body: "", Header: make(http.Header)} wrapper := ResponseWrapper{StatusCode: 0, Body: "", Header: make(http.Header)}
client := &http.Client{} client := &http.Client{}
if timeout > 0 { if timeout > 0 {
client.Timeout = time.Duration(timeout) * time.Second client.Timeout = time.Duration(timeout) * time.Second
} }
setRequestHeader(req) setRequestHeader(req)
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
wrapper.Body = fmt.Sprintf("执行HTTP请求错误-%s", err.Error()) wrapper.Body = fmt.Sprintf("执行HTTP请求错误-%s", err.Error())
return wrapper return wrapper
} }
defer resp.Body.Close() defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body) body, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
wrapper.Body = fmt.Sprintf("读取HTTP请求返回值失败-%s", err.Error()) wrapper.Body = fmt.Sprintf("读取HTTP请求返回值失败-%s", err.Error())
return wrapper return wrapper
} }
wrapper.StatusCode = resp.StatusCode wrapper.StatusCode = resp.StatusCode
wrapper.Body = string(body) wrapper.Body = string(body)
wrapper.Header = resp.Header wrapper.Header = resp.Header
return wrapper return wrapper
} }
func setRequestHeader(req *http.Request) { func setRequestHeader(req *http.Request) {
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.8,en;q=0.6") 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") 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 { func createRequestError(err error) ResponseWrapper {
errorMessage := fmt.Sprintf("创建HTTP请求错误-%s", err.Error()) errorMessage := fmt.Sprintf("创建HTTP请求错误-%s", err.Error())
return ResponseWrapper{0, errorMessage, make(http.Header)} return ResponseWrapper{0, errorMessage, make(http.Header)}
} }

View File

@ -1,11 +1,11 @@
package logger package logger
import ( import (
"github.com/cihub/seelog" "fmt"
"gopkg.in/macaron.v1" "github.com/cihub/seelog"
"fmt" "gopkg.in/macaron.v1"
"os" "os"
"runtime" "runtime"
) )
// 日志库 // 日志库
@ -22,113 +22,113 @@ const (
FATAL FATAL
) )
func InitLogger() { func InitLogger() {
config := getLogConfig() config := getLogConfig()
l, err := seelog.LoggerFromConfigAsString(config) l, err := seelog.LoggerFromConfigAsString(config)
if err != nil { if err != nil {
panic(err) panic(err)
} }
logger = l logger = l
} }
func Debug(v ...interface{}) { func Debug(v ...interface{}) {
if macaron.Env != macaron.DEV { if macaron.Env != macaron.DEV {
return return
} }
write(DEBUG, v) write(DEBUG, v)
} }
func Debugf(format string, v ...interface{}) { func Debugf(format string, v ...interface{}) {
if macaron.Env != macaron.DEV { if macaron.Env != macaron.DEV {
return return
} }
writef(DEBUG, format, v...) writef(DEBUG, format, v...)
} }
func Info(v ...interface{}) { func Info(v ...interface{}) {
write(INFO, v) write(INFO, v)
} }
func Infof(format string, v ...interface{}) { func Infof(format string, v ...interface{}) {
writef(INFO, format, v...) writef(INFO, format, v...)
} }
func Warn(v ...interface{}) { func Warn(v ...interface{}) {
write(WARN, v) write(WARN, v)
} }
func Warnf(format string, v ...interface{}) { func Warnf(format string, v ...interface{}) {
writef(WARN, format, v...) writef(WARN, format, v...)
} }
func Error(v ...interface{}) { func Error(v ...interface{}) {
write(ERROR, v) write(ERROR, v)
} }
func Errorf(format string, v ...interface{}) { func Errorf(format string, v ...interface{}) {
writef(ERROR, format, v...) writef(ERROR, format, v...)
} }
func Fatal(v ...interface{}) { func Fatal(v ...interface{}) {
write(FATAL, v) write(FATAL, v)
} }
func Fatalf(format string, v ...interface{}) { func Fatalf(format string, v ...interface{}) {
writef(FATAL, format, v...) writef(FATAL, format, v...)
} }
func write(level Level, v ...interface{}) { func write(level Level, v ...interface{}) {
defer logger.Flush() defer logger.Flush()
content := "" content := ""
if macaron.Env == macaron.DEV { if macaron.Env == macaron.DEV {
pc, file, line, ok := runtime.Caller(2) pc, file, line, ok := runtime.Caller(2)
if ok { if ok {
content = fmt.Sprintf("#%s#%s#%d行#", file, runtime.FuncForPC(pc).Name(), line) content = fmt.Sprintf("#%s#%s#%d行#", file, runtime.FuncForPC(pc).Name(), line)
} }
} }
switch level { switch level {
case DEBUG: case DEBUG:
logger.Debug(content, v) logger.Debug(content, v)
case INFO: case INFO:
logger.Info(content, v) logger.Info(content, v)
case WARN: case WARN:
logger.Warn(content, v) logger.Warn(content, v)
case FATAL: case FATAL:
logger.Critical(content, v) logger.Critical(content, v)
os.Exit(1) os.Exit(1)
case ERROR: case ERROR:
logger.Error(content, v) logger.Error(content, v)
} }
} }
func writef(level Level, format string, v ...interface{}) { func writef(level Level, format string, v ...interface{}) {
defer logger.Flush() defer logger.Flush()
content := "" content := ""
if macaron.Env == macaron.DEV { if macaron.Env == macaron.DEV {
pc, file, line, ok := runtime.Caller(2) pc, file, line, ok := runtime.Caller(2)
if ok { if ok {
content = fmt.Sprintf("#%s#%s#%d行#", file, runtime.FuncForPC(pc).Name(), line) content = fmt.Sprintf("#%s#%s#%d行#", file, runtime.FuncForPC(pc).Name(), line)
} }
} }
format = content + format format = content + format
switch level { switch level {
case DEBUG: case DEBUG:
logger.Debugf(format, v...) logger.Debugf(format, v...)
case INFO: case INFO:
logger.Infof(format, v...) logger.Infof(format, v...)
case WARN: case WARN:
logger.Warnf(format, v...) logger.Warnf(format, v...)
case FATAL: case FATAL:
logger.Criticalf(format, v...) logger.Criticalf(format, v...)
os.Exit(1) os.Exit(1)
case ERROR: case ERROR:
logger.Errorf(format, v...) logger.Errorf(format, v...)
} }
} }
func getLogConfig() string { func getLogConfig() string {
@ -145,16 +145,16 @@ func getLogConfig() string {
</formats> </formats>
</seelog>` </seelog>`
consoleConfig := "" consoleConfig := ""
if macaron.Env == macaron.DEV { if macaron.Env == macaron.DEV {
consoleConfig = consoleConfig =
` `
<filter levels="info,debug,critical,warn,error"> <filter levels="info,debug,critical,warn,error">
<console /> <console />
</filter> </filter>
` `
} }
config = fmt.Sprintf(config, consoleConfig) config = fmt.Sprintf(config, consoleConfig)
return config return config
} }

View File

@ -1,84 +1,82 @@
package notify package notify
import ( import (
"github.com/ouqiang/gocron/models" "github.com/go-gomail/gomail"
"github.com/ouqiang/gocron/modules/logger" "github.com/ouqiang/gocron/models"
"strconv" "github.com/ouqiang/gocron/modules/logger"
"strings" "github.com/ouqiang/gocron/modules/utils"
"github.com/ouqiang/gocron/modules/utils" "strconv"
"time" "strings"
"github.com/go-gomail/gomail" "time"
) )
// @author qiang.ou<qingqianludao@gmail.com> // @author qiang.ou<qingqianludao@gmail.com>
// @date 2017/5/1-00:19 // @date 2017/5/1-00:19
type Mail struct { type Mail struct {
} }
func (mail *Mail) Send(msg Message) { func (mail *Mail) Send(msg Message) {
model := new(models.Setting) model := new(models.Setting)
mailSetting, err := model.Mail() mailSetting, err := model.Mail()
logger.Debugf("%+v", mailSetting) logger.Debugf("%+v", mailSetting)
if err != nil { if err != nil {
logger.Error("#mail#从数据库获取mail配置失败", err) logger.Error("#mail#从数据库获取mail配置失败", err)
return return
} }
if mailSetting.Host == "" { if mailSetting.Host == "" {
logger.Error("#mail#Host为空") logger.Error("#mail#Host为空")
return return
} }
if mailSetting.Port == 0 { if mailSetting.Port == 0 {
logger.Error("#mail#Port为空") logger.Error("#mail#Port为空")
return return
} }
if mailSetting.User == "" { if mailSetting.User == "" {
logger.Error("#mail#User为空") logger.Error("#mail#User为空")
return return
} }
if mailSetting.Password == "" { if mailSetting.Password == "" {
logger.Error("#mail#Password为空") logger.Error("#mail#Password为空")
return return
} }
toUsers := mail.getActiveMailUsers(mailSetting, msg) toUsers := mail.getActiveMailUsers(mailSetting, msg)
mail.send(mailSetting, toUsers, msg) mail.send(mailSetting, toUsers, msg)
} }
func (mail *Mail) send(mailSetting models.Mail, toUsers []string, msg Message) { func (mail *Mail) send(mailSetting models.Mail, toUsers []string, msg Message) {
body := msg["content"].(string) body := msg["content"].(string)
body = strings.Replace(body, "\n", "<br>", -1) body = strings.Replace(body, "\n", "<br>", -1)
gomailMessage := gomail.NewMessage() gomailMessage := gomail.NewMessage()
gomailMessage.SetHeader("From", mailSetting.User) gomailMessage.SetHeader("From", mailSetting.User)
gomailMessage.SetHeader("To", toUsers...) gomailMessage.SetHeader("To", toUsers...)
gomailMessage.SetHeader("Subject", "gocron-定时任务监控通知") gomailMessage.SetHeader("Subject", "gocron-定时任务监控通知")
gomailMessage.SetBody("text/html", body) gomailMessage.SetBody("text/html", body)
mailer := gomail.NewPlainDialer(mailSetting.Host, mailSetting.Port, mailer := gomail.NewPlainDialer(mailSetting.Host, mailSetting.Port,
mailSetting.User, mailSetting.Password) mailSetting.User, mailSetting.Password)
maxTimes := 3 maxTimes := 3
i := 0 i := 0
for i < maxTimes { for i < maxTimes {
err := mailer.DialAndSend(gomailMessage) err := mailer.DialAndSend(gomailMessage)
if err == nil { if err == nil {
break; break
} }
i += 1 i += 1
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
if i < maxTimes { if i < maxTimes {
logger.Errorf("mail#发送消息失败#%s#消息内容-%s", err.Error(), msg["content"]) logger.Errorf("mail#发送消息失败#%s#消息内容-%s", err.Error(), msg["content"])
} }
} }
} }
func (mail *Mail) getActiveMailUsers(mailSetting models.Mail, msg Message) []string { func (mail *Mail) getActiveMailUsers(mailSetting models.Mail, msg Message) []string {
taskReceiverIds := strings.Split(msg["task_receiver_id"].(string), ",") taskReceiverIds := strings.Split(msg["task_receiver_id"].(string), ",")
users := []string{} users := []string{}
for _, v := range(mailSetting.MailUsers) { for _, v := range mailSetting.MailUsers {
if utils.InStringSlice(taskReceiverIds, strconv.Itoa(v.Id)) { if utils.InStringSlice(taskReceiverIds, strconv.Itoa(v.Id)) {
users = append(users, v.Email) users = append(users, v.Email)
} }
} }
return users return users
} }

View File

@ -1,52 +1,52 @@
package notify package notify
import ( import (
"time" "fmt"
"github.com/ouqiang/gocron/modules/logger" "github.com/ouqiang/gocron/modules/logger"
"fmt" "time"
) )
type Message map[string]interface{} type Message map[string]interface{}
type Notifiable interface { type Notifiable interface {
Send(msg Message) Send(msg Message)
} }
var queue chan Message = make(chan Message, 100) var queue chan Message = make(chan Message, 100)
func init() { func init() {
go run() go run()
} }
// 把消息推入队列 // 把消息推入队列
func Push(msg Message) { func Push(msg Message) {
queue <- msg queue <- msg
} }
func run() { func run() {
for msg := range queue { for msg := range queue {
// 根据任务配置发送通知 // 根据任务配置发送通知
taskType, taskTypeOk := msg["task_type"] taskType, taskTypeOk := msg["task_type"]
_, taskReceiverIdOk := msg["task_receiver_id"] _, taskReceiverIdOk := msg["task_receiver_id"]
_, nameOk := msg["name"] _, nameOk := msg["name"]
_, outputOk := msg["output"] _, outputOk := msg["output"]
_, statusOk := msg["status"] _, statusOk := msg["status"]
if !taskTypeOk || !taskReceiverIdOk || !nameOk || !outputOk || !statusOk { if !taskTypeOk || !taskReceiverIdOk || !nameOk || !outputOk || !statusOk {
logger.Errorf("#notify#参数不完整#%+v", msg) logger.Errorf("#notify#参数不完整#%+v", msg)
continue continue
} }
msg["content"] = fmt.Sprintf("============\n============\n============\n任务名称: %s\n状态: %s\n输出:\n %s\n", msg["name"], msg["status"], msg["output"]) msg["content"] = fmt.Sprintf("============\n============\n============\n任务名称: %s\n状态: %s\n输出:\n %s\n", msg["name"], msg["status"], msg["output"])
logger.Debugf("%+v", msg) logger.Debugf("%+v", msg)
switch(taskType.(int8)) { switch taskType.(int8) {
case 1: case 1:
// 邮件 // 邮件
mail := Mail{} mail := Mail{}
go mail.Send(msg) go mail.Send(msg)
case 2: case 2:
// Slack // Slack
slack := Slack{} slack := Slack{}
go slack.Send(msg) go slack.Send(msg)
} }
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
} }
} }

View File

@ -1,78 +1,79 @@
package notify package notify
// 发送消息到slack // 发送消息到slack
import ( import (
"fmt" "fmt"
"github.com/ouqiang/gocron/modules/httpclient" "github.com/ouqiang/gocron/models"
"github.com/ouqiang/gocron/modules/logger" "github.com/ouqiang/gocron/modules/httpclient"
"github.com/ouqiang/gocron/modules/utils" "github.com/ouqiang/gocron/modules/logger"
"strings" "github.com/ouqiang/gocron/modules/utils"
"github.com/ouqiang/gocron/models" "strconv"
"strconv" "strings"
"time" "time"
) )
type Slack struct {} type Slack struct{}
func (slack *Slack) Send(msg Message) { func (slack *Slack) Send(msg Message) {
model := new(models.Setting) model := new(models.Setting)
slackSetting, err := model.Slack() slackSetting, err := model.Slack()
if err != nil { if err != nil {
logger.Error("#slack#从数据库获取slack配置失败", err) logger.Error("#slack#从数据库获取slack配置失败", err)
return return
} }
if slackSetting.Url == "" { if slackSetting.Url == "" {
logger.Error("#slack#webhook-url为空") logger.Error("#slack#webhook-url为空")
return return
} }
if len(slackSetting.Channels) == 0 { if len(slackSetting.Channels) == 0 {
logger.Error("#slack#channels配置为空") logger.Error("#slack#channels配置为空")
return return
} }
logger.Debugf("%+v", slackSetting) logger.Debugf("%+v", slackSetting)
channels := slack.getActiveSlackChannels(slackSetting, msg) channels := slack.getActiveSlackChannels(slackSetting, msg)
logger.Debugf("%+v", channels) logger.Debugf("%+v", channels)
for _, channel := range(channels) { for _, channel := range channels {
slack.send(msg, slackSetting.Url, channel) slack.send(msg, slackSetting.Url, channel)
} }
} }
func (slack *Slack) send(msg Message, slackUrl string, channel string) { func (slack *Slack) send(msg Message, slackUrl string, channel string) {
formatBody := slack.format(msg["content"].(string), channel) formatBody := slack.format(msg["content"].(string), channel)
timeout := 30 timeout := 30
maxTimes := 3 maxTimes := 3
i := 0 i := 0
for i < maxTimes { for i < maxTimes {
resp := httpclient.PostJson(slackUrl, formatBody, timeout) resp := httpclient.PostJson(slackUrl, formatBody, timeout)
if resp.StatusCode == 200 { if resp.StatusCode == 200 {
break; break
} }
i += 1 i += 1
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
if i < maxTimes { if i < maxTimes {
logger.Errorf("slack#发送消息失败#%s#消息内容-%s", resp.Body, msg["content"]) logger.Errorf("slack#发送消息失败#%s#消息内容-%s", resp.Body, msg["content"])
} }
} }
} }
func (slack *Slack) getActiveSlackChannels(slackSetting models.Slack, msg Message) []string { func (slack *Slack) getActiveSlackChannels(slackSetting models.Slack, msg Message) []string {
taskReceiverIds := strings.Split(msg["task_receiver_id"].(string), ",") taskReceiverIds := strings.Split(msg["task_receiver_id"].(string), ",")
channels := []string{} channels := []string{}
for _, v := range(slackSetting.Channels) { for _, v := range slackSetting.Channels {
if utils.InStringSlice(taskReceiverIds, strconv.Itoa(v.Id)) { if utils.InStringSlice(taskReceiverIds, strconv.Itoa(v.Id)) {
channels = append(channels, v.Name) channels = append(channels, v.Name)
} }
} }
return channels return channels
} }
// 格式化消息内容 // 格式化消息内容
func (slack *Slack) format(content string, channel string) string { func (slack *Slack) format(content string, channel string) string {
content = utils.EscapeJson(content) content = utils.EscapeJson(content)
specialChars := []string{"&", "<", ">"} specialChars := []string{"&", "<", ">"}
replaceChars := []string{"&amp;", "&lt;", "&gt;"} replaceChars := []string{"&amp;", "&lt;", "&gt;"}
content = utils.ReplaceStrings(content, specialChars, replaceChars) content = utils.ReplaceStrings(content, specialChars, replaceChars)
return fmt.Sprintf(`{"text":"%s","username":"监控", "channel":"%s"}`, content, channel) return fmt.Sprintf(`{"text":"%s","username":"监控", "channel":"%s"}`, content, channel)
} }

View File

@ -3,16 +3,16 @@ package auth
import ( import (
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"io/ioutil"
"errors" "errors"
"fmt" "fmt"
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
"io/ioutil"
) )
type Certificate struct { type Certificate struct {
CAFile string CAFile string
CertFile string CertFile string
KeyFile string KeyFile string
ServerName string ServerName string
} }
@ -33,14 +33,12 @@ func (c Certificate) GetTLSConfigForServer() (*tls.Config, error) {
return nil, errors.New("failed to append client certs") return nil, errors.New("failed to append client certs")
} }
tlsConfig := &tls.Config{ tlsConfig := &tls.Config{
ClientAuth: tls.RequireAndVerifyClientCert, ClientAuth: tls.RequireAndVerifyClientCert,
Certificates: []tls.Certificate{certificate}, Certificates: []tls.Certificate{certificate},
ClientCAs: certPool, ClientCAs: certPool,
} }
return tlsConfig, nil return tlsConfig, nil
} }
@ -68,4 +66,4 @@ func (c Certificate) GetTransportCredsForClient() (credentials.TransportCredenti
}) })
return transportCreds, nil return transportCreds, nil
} }

View File

@ -1,80 +1,65 @@
package client package client
import ( import (
pb "github.com/ouqiang/gocron/modules/rpc/proto" "errors"
"golang.org/x/net/context" "fmt"
"fmt" "github.com/ouqiang/gocron/modules/logger"
"time" "github.com/ouqiang/gocron/modules/rpc/grpcpool"
"errors" pb "github.com/ouqiang/gocron/modules/rpc/proto"
"github.com/ouqiang/gocron/modules/rpc/grpcpool" "golang.org/x/net/context"
"google.golang.org/grpc/codes" "google.golang.org/grpc"
"google.golang.org/grpc" "google.golang.org/grpc/codes"
"github.com/ouqiang/gocron/modules/logger" "time"
) )
var ( var (
errUnavailable = errors.New("无法连接远程服务器") errUnavailable = errors.New("无法连接远程服务器")
) )
func ExecWithRetry(ip string, port int, taskReq *pb.TaskRequest) (string, error) { func Exec(ip string, port int, taskReq *pb.TaskRequest) (string, error) {
tryTimes := 60 defer func() {
i := 0 if err := recover(); err != nil {
for i < tryTimes { logger.Error("panic#rpc/client.go:Exec#", err)
output, err := Exec(ip, port, taskReq) }
if err != errUnavailable { }()
return output, err addr := fmt.Sprintf("%s:%d", ip, port)
} conn, err := grpcpool.Pool.Get(addr)
i++ if err != nil {
time.Sleep(2 * time.Second) 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) { return resp.Output, errors.New(resp.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)
} }
func parseGRPCError(err error, conn *grpc.ClientConn, connClosed *bool) (string, error) { func parseGRPCError(err error, conn *grpc.ClientConn, connClosed *bool) (string, error) {
switch grpc.Code(err) { switch grpc.Code(err) {
case codes.Unavailable, codes.Internal: case codes.Unavailable, codes.Internal:
conn.Close() conn.Close()
*connClosed = true *connClosed = true
return "", errUnavailable return "", errUnavailable
case codes.DeadlineExceeded: case codes.DeadlineExceeded:
return "", errors.New("执行超时, 强制结束") return "", errors.New("执行超时, 强制结束")
} }
return "", err return "", err
} }

View File

@ -1,141 +1,139 @@
package grpcpool package grpcpool
import ( import (
"github.com/silenceper/pool" "errors"
"sync" "github.com/ouqiang/gocron/modules/app"
"time" "github.com/ouqiang/gocron/modules/rpc/auth"
"google.golang.org/grpc" "github.com/silenceper/pool"
"errors" "google.golang.org/grpc"
"github.com/ouqiang/gocron/modules/rpc/auth" "strings"
"github.com/ouqiang/gocron/modules/app" "sync"
"strings" "time"
)
var (
Pool GRPCPool
) )
var ( var (
ErrInvalidConn = errors.New("invalid connection") Pool GRPCPool
) )
func init() { var (
Pool = GRPCPool{ ErrInvalidConn = errors.New("invalid connection")
make(map[string]pool.Pool), )
sync.RWMutex{},
} func init() {
Pool = GRPCPool{
make(map[string]pool.Pool),
sync.RWMutex{},
}
} }
type GRPCPool struct { type GRPCPool struct {
// map key格式 ip:port // map key格式 ip:port
conns map[string]pool.Pool conns map[string]pool.Pool
sync.RWMutex sync.RWMutex
} }
func (p *GRPCPool) Get(addr string) (*grpc.ClientConn, error) { func (p *GRPCPool) Get(addr string) (*grpc.ClientConn, error) {
p.RLock() p.RLock()
pool, ok := p.conns[addr] pool, ok := p.conns[addr]
p.RUnlock() p.RUnlock()
if !ok { if !ok {
err := p.newCommonPool(addr) err := p.newCommonPool(addr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
p.RLock() p.RLock()
pool = p.conns[addr] pool = p.conns[addr]
p.RUnlock() p.RUnlock()
conn, err := pool.Get() conn, err := pool.Get()
if err != nil { if err != nil {
return nil, err return nil, err
} }
return conn.(*grpc.ClientConn), nil return conn.(*grpc.ClientConn), nil
} }
func (p *GRPCPool) Put(addr string, conn *grpc.ClientConn) error { func (p *GRPCPool) Put(addr string, conn *grpc.ClientConn) error {
p.RLock() p.RLock()
defer p.RUnlock() defer p.RUnlock()
pool, ok := p.conns[addr] pool, ok := p.conns[addr]
if ok { if ok {
return pool.Put(conn) return pool.Put(conn)
} }
return ErrInvalidConn return ErrInvalidConn
} }
// 释放连接池 // 释放连接池
func (p *GRPCPool) Release(addr string) { func (p *GRPCPool) Release(addr string) {
p.Lock() p.Lock()
defer p.Unlock() defer p.Unlock()
pool, ok := p.conns[addr] pool, ok := p.conns[addr]
if !ok { if !ok {
return return
} }
pool.Release() pool.Release()
delete(p.conns, addr) delete(p.conns, addr)
} }
// 释放所有连接池 // 释放所有连接池
func (p *GRPCPool) ReleaseAll() { func (p *GRPCPool) ReleaseAll() {
p.Lock() p.Lock()
defer p.Unlock() defer p.Unlock()
for _, pool := range(p.conns) { for _, pool := range p.conns {
pool.Release() pool.Release()
} }
} }
// 初始化底层连接池 // 初始化底层连接池
func (p *GRPCPool) newCommonPool(addr string) (error) { func (p *GRPCPool) newCommonPool(addr string) error {
p.Lock() p.Lock()
defer p.Unlock() defer p.Unlock()
commonPool, ok := p.conns[addr] commonPool, ok := p.conns[addr]
if ok { if ok {
return nil return nil
} }
poolConfig := &pool.PoolConfig{ poolConfig := &pool.PoolConfig{
InitialCap: 1, InitialCap: 1,
MaxCap: 30, MaxCap: 30,
Factory: func() (interface{}, error) { Factory: func() (interface{}, error) {
if !app.Setting.EnableTLS { if !app.Setting.EnableTLS {
return grpc.Dial(addr, grpc.WithInsecure()) return grpc.Dial(addr, grpc.WithInsecure())
} }
server := strings.Split(addr, ":") server := strings.Split(addr, ":")
certificate := auth.Certificate{ certificate := auth.Certificate{
CAFile: app.Setting.CAFile, CAFile: app.Setting.CAFile,
CertFile: app.Setting.CertFile, CertFile: app.Setting.CertFile,
KeyFile: app.Setting.KeyFile, KeyFile: app.Setting.KeyFile,
ServerName: server[0], ServerName: server[0],
} }
transportCreds, err := certificate.GetTransportCredsForClient() transportCreds, err := certificate.GetTransportCredsForClient()
if err != nil { if err != nil {
return nil, err return nil, err
} }
return grpc.Dial(addr, grpc.WithTransportCredentials(transportCreds)) return grpc.Dial(addr, grpc.WithTransportCredentials(transportCreds))
}, },
Close: func(v interface{}) error { Close: func(v interface{}) error {
conn, ok := v.(*grpc.ClientConn) conn, ok := v.(*grpc.ClientConn)
if ok && conn != nil { if ok && conn != nil {
return conn.Close() return conn.Close()
} }
return ErrInvalidConn return ErrInvalidConn
}, },
IdleTimeout: 3 * time.Minute, IdleTimeout: 3 * time.Minute,
} }
commonPool, err := pool.NewChannelPool(poolConfig) commonPool, err := pool.NewChannelPool(poolConfig)
if err != nil { if err != nil {
return err return err
} }
p.conns[addr] = commonPool p.conns[addr] = commonPool
return nil return nil
} }

View File

@ -1,65 +1,64 @@
package server package server
import ( import (
"golang.org/x/net/context" "github.com/ouqiang/gocron/modules/rpc/auth"
"net" pb "github.com/ouqiang/gocron/modules/rpc/proto"
"google.golang.org/grpc/grpclog" "github.com/ouqiang/gocron/modules/utils"
"google.golang.org/grpc" "golang.org/x/net/context"
pb "github.com/ouqiang/gocron/modules/rpc/proto" "google.golang.org/grpc"
"github.com/ouqiang/gocron/modules/utils" "google.golang.org/grpc/credentials"
"github.com/ouqiang/gocron/modules/rpc/auth" "google.golang.org/grpc/grpclog"
"google.golang.org/grpc/credentials" "net"
) )
type Server struct {} type Server struct{}
func (s Server) Run(ctx context.Context, req *pb.TaskRequest) (*pb.TaskResponse, error) { func (s Server) Run(ctx context.Context, req *pb.TaskRequest) (*pb.TaskResponse, error) {
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
grpclog.Println(err) grpclog.Println(err)
} }
} () }()
output, err := utils.ExecShell(ctx, req.Command) output, err := utils.ExecShell(ctx, req.Command)
resp := new(pb.TaskResponse) resp := new(pb.TaskResponse)
resp.Output = output resp.Output = output
if err != nil { if err != nil {
resp.Error = err.Error() resp.Error = err.Error()
} else { } else {
resp.Error = "" resp.Error = ""
} }
return resp, nil return resp, nil
} }
func Start(addr string, enableTLS bool, certificate auth.Certificate) { func Start(addr string, enableTLS bool, certificate auth.Certificate) {
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
grpclog.Println("panic", err) grpclog.Println("panic", err)
} }
} () }()
l, err := net.Listen("tcp", addr) l, err := net.Listen("tcp", addr)
if err != nil { if err != nil {
grpclog.Fatal(err) grpclog.Fatal(err)
} }
var s *grpc.Server var s *grpc.Server
if enableTLS { if enableTLS {
tlsConfig, err := certificate.GetTLSConfigForServer() tlsConfig, err := certificate.GetTLSConfigForServer()
if err != nil { if err != nil {
grpclog.Fatal(err) grpclog.Fatal(err)
} }
opt := grpc.Creds(credentials.NewTLS(tlsConfig)) opt := grpc.Creds(credentials.NewTLS(tlsConfig))
s = grpc.NewServer(opt) s = grpc.NewServer(opt)
pb.RegisterTaskServer(s, Server{}) pb.RegisterTaskServer(s, Server{})
grpclog.Printf("listen %s with TLS", addr) grpclog.Printf("listen %s with TLS", addr)
} else { } else {
s = grpc.NewServer() s = grpc.NewServer()
pb.RegisterTaskServer(s, Server{}) pb.RegisterTaskServer(s, Server{})
grpclog.Printf("listen %s", addr) grpclog.Printf("listen %s", addr)
} }
err = s.Serve(l) err = s.Serve(l)
grpclog.Fatal(err) grpclog.Fatal(err)
} }

View File

@ -1,111 +1,111 @@
package setting package setting
import ( import (
"errors" "errors"
"gopkg.in/ini.v1" "github.com/ouqiang/gocron/modules/logger"
"github.com/ouqiang/gocron/modules/utils" "github.com/ouqiang/gocron/modules/utils"
"github.com/ouqiang/gocron/modules/logger" "gopkg.in/ini.v1"
) )
const DefaultSection = "default" const DefaultSection = "default"
type Setting struct { type Setting struct {
Db struct{ Db struct {
Engine string Engine string
Host string Host string
Port int Port int
User string User string
Password string Password string
Database string Database string
Prefix string Prefix string
Charset string Charset string
MaxIdleConns int MaxIdleConns int
MaxOpenConns int MaxOpenConns int
} }
AllowIps string AllowIps string
AppName string AppName string
ApiKey string ApiKey string
ApiSecret string ApiSecret string
ApiSignEnable bool ApiSignEnable bool
EnableTLS bool EnableTLS bool
CAFile string CAFile string
CertFile string CertFile string
KeyFile string KeyFile string
} }
// 读取配置 // 读取配置
func Read(filename string) (*Setting,error) { func Read(filename string) (*Setting, error) {
config, err := ini.Load(filename) config, err := ini.Load(filename)
if err != nil { if err != nil {
return nil, err return nil, err
} }
section := config.Section(DefaultSection) section := config.Section(DefaultSection)
var s Setting var s Setting
s.Db.Engine = section.Key("db.engine").MustString("mysql") s.Db.Engine = section.Key("db.engine").MustString("mysql")
s.Db.Host = section.Key("db.host").MustString("127.0.0.1") s.Db.Host = section.Key("db.host").MustString("127.0.0.1")
s.Db.Port = section.Key("db.port").MustInt(3306) s.Db.Port = section.Key("db.port").MustInt(3306)
s.Db.User = section.Key("db.user").MustString("") s.Db.User = section.Key("db.user").MustString("")
s.Db.Password = section.Key("db.password").MustString("") s.Db.Password = section.Key("db.password").MustString("")
s.Db.Database = section.Key("db.database").MustString("gocron") s.Db.Database = section.Key("db.database").MustString("gocron")
s.Db.Prefix = section.Key("db.prefix").MustString("") s.Db.Prefix = section.Key("db.prefix").MustString("")
s.Db.Charset = section.Key("db.charset").MustString("utf8") s.Db.Charset = section.Key("db.charset").MustString("utf8")
s.Db.MaxIdleConns = section.Key("db.max.idle.conns").MustInt(30) s.Db.MaxIdleConns = section.Key("db.max.idle.conns").MustInt(30)
s.Db.MaxOpenConns = section.Key("db.max.open.conns").MustInt(100) s.Db.MaxOpenConns = section.Key("db.max.open.conns").MustInt(100)
s.AllowIps = section.Key("allow_ips").MustString("") s.AllowIps = section.Key("allow_ips").MustString("")
s.AppName = section.Key("app.name").MustString("定时任务管理系统") s.AppName = section.Key("app.name").MustString("定时任务管理系统")
s.ApiKey = section.Key("api.key").MustString("") s.ApiKey = section.Key("api.key").MustString("")
s.ApiSecret = section.Key("api.secret").MustString("") s.ApiSecret = section.Key("api.secret").MustString("")
s.ApiSignEnable = section.Key("api.sign.enable").MustBool(true) s.ApiSignEnable = section.Key("api.sign.enable").MustBool(true)
s.EnableTLS = section.Key("enable_tls").MustBool(false) s.EnableTLS = section.Key("enable_tls").MustBool(false)
s.CAFile = section.Key("ca_file").MustString("") s.CAFile = section.Key("ca_file").MustString("")
s.CertFile = section.Key("cert_file").MustString("") s.CertFile = section.Key("cert_file").MustString("")
s.KeyFile = section.Key("key_file").MustString("") s.KeyFile = section.Key("key_file").MustString("")
if s.EnableTLS { if s.EnableTLS {
if !utils.FileExist(s.CAFile) { if !utils.FileExist(s.CAFile) {
logger.Fatalf("failed to read ca cert file: %s", s.CAFile) logger.Fatalf("failed to read ca cert file: %s", s.CAFile)
} }
if !utils.FileExist(s.CertFile) { if !utils.FileExist(s.CertFile) {
logger.Fatalf("failed to read client cert file: %s", s.CertFile) logger.Fatalf("failed to read client cert file: %s", s.CertFile)
} }
if !utils.FileExist(s.KeyFile) { if !utils.FileExist(s.KeyFile) {
logger.Fatalf("failed to read client key file: %s", 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 { func Write(config []string, filename string) error {
if len(config) == 0 { if len(config) == 0 {
return errors.New("参数不能为空") return errors.New("参数不能为空")
} }
if len(config) % 2 != 0 { if len(config)%2 != 0 {
return errors.New("参数不匹配") return errors.New("参数不匹配")
} }
file := ini.Empty() file := ini.Empty()
section, err := file.NewSection(DefaultSection) section, err := file.NewSection(DefaultSection)
if err != nil { if err != nil {
return err return err
} }
for i := 0 ;i < len(config); { for i := 0; i < len(config); {
_, err = section.NewKey(config[i], config[i+1]) _, err = section.NewKey(config[i], config[i+1])
if err != nil { if err != nil {
return err return err
} }
i += 2 i += 2
} }
err = file.SaveTo(filename) err = file.SaveTo(filename)
return err return err
} }

View File

@ -1,143 +1,139 @@
package ssh package ssh
import ( import (
"golang.org/x/crypto/ssh" "errors"
"fmt" "fmt"
"net" "golang.org/x/crypto/ssh"
"time" "net"
"errors" "time"
) )
type HostAuthType int8 // 认证方式 type HostAuthType int8 // 认证方式
const ( const (
HostPassword = 1 // 密码认证 HostPassword = 1 // 密码认证
HostPublicKey = 2 // 公钥认证 HostPublicKey = 2 // 公钥认证
) )
const SSHConnectTimeout = 10 const SSHConnectTimeout = 10
type SSHConfig struct {
type SSHConfig struct { AuthType HostAuthType
AuthType HostAuthType User string
User string Password string
Password string PrivateKey string
PrivateKey string Host string
Host string Port int
Port int ExecTimeout int // 执行超时时间
ExecTimeout int// 执行超时时间
} }
type Result struct { type Result struct {
Output string Output string
Err error Err error
} }
func parseSSHConfig(sshConfig SSHConfig) (config *ssh.ClientConfig, err error) { func parseSSHConfig(sshConfig SSHConfig) (config *ssh.ClientConfig, err error) {
timeout := time.Duration(SSHConnectTimeout) * time.Second timeout := time.Duration(SSHConnectTimeout) * time.Second
// 密码认证 // 密码认证
if sshConfig.AuthType == HostPassword { if sshConfig.AuthType == HostPassword {
config = &ssh.ClientConfig{ config = &ssh.ClientConfig{
User: sshConfig.User, User: sshConfig.User,
Auth: []ssh.AuthMethod{ Auth: []ssh.AuthMethod{
ssh.Password(sshConfig.Password), ssh.Password(sshConfig.Password),
}, },
Timeout: timeout, Timeout: timeout,
HostKeyCallback:func(hostname string, remote net.Addr, key ssh.PublicKey) error { HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil return nil
}, },
} }
return return
} }
signer, err := ssh.ParsePrivateKey([]byte(sshConfig.PrivateKey)) signer, err := ssh.ParsePrivateKey([]byte(sshConfig.PrivateKey))
if err != nil { if err != nil {
return return
} }
// 公钥认证 // 公钥认证
config = &ssh.ClientConfig{ config = &ssh.ClientConfig{
User: sshConfig.User, User: sshConfig.User,
Auth: []ssh.AuthMethod{ Auth: []ssh.AuthMethod{
ssh.PublicKeys(signer), ssh.PublicKeys(signer),
}, },
Timeout: timeout, Timeout: timeout,
HostKeyCallback:func(hostname string, remote net.Addr, key ssh.PublicKey) error { HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil return nil
}, },
} }
return return
} }
// 执行shell命令 // 执行shell命令
func Exec(sshConfig SSHConfig, cmd string) (output string, err error) { func Exec(sshConfig SSHConfig, cmd string) (output string, err error) {
client, err := getClient(sshConfig) client, err := getClient(sshConfig)
if err != nil { if err != nil {
return "", err return "", err
} }
defer client.Close() defer client.Close()
session, err := client.NewSession() session, err := client.NewSession()
if err != nil { if err != nil {
return "", err return "", err
} }
defer session.Close() defer session.Close()
// 后台运行 // 后台运行
if sshConfig.ExecTimeout < 0 { if sshConfig.ExecTimeout < 0 {
go session.CombinedOutput(cmd) go session.CombinedOutput(cmd)
time.Sleep(10 * time.Second) time.Sleep(10 * time.Second)
return "", nil return "", nil
} }
// 不限制超时 // 不限制超时
if sshConfig.ExecTimeout == 0 { if sshConfig.ExecTimeout == 0 {
outputByte, execErr := session.CombinedOutput(cmd) outputByte, execErr := session.CombinedOutput(cmd)
output = string(outputByte) output = string(outputByte)
err = execErr err = execErr
return return
} }
var resultChan chan Result = make(chan Result) var resultChan chan Result = make(chan Result)
var timeoutChan chan bool = make(chan bool) var timeoutChan chan bool = make(chan bool)
go func() { go func() {
output, err := session.CombinedOutput(cmd) output, err := session.CombinedOutput(cmd)
resultChan <- Result{string(output), err} resultChan <- Result{string(output), err}
}() }()
// todo 等待超时后,如何停止远程正在执行的任务, 使用timeout命令但不具有通用性 // todo 等待超时后,如何停止远程正在执行的任务, 使用timeout命令但不具有通用性
go triggerTimeout(timeoutChan, sshConfig.ExecTimeout) go triggerTimeout(timeoutChan, sshConfig.ExecTimeout)
select { select {
case result := <- resultChan: case result := <-resultChan:
output = result.Output output = result.Output
err = result.Err err = result.Err
case <- timeoutChan: case <-timeoutChan:
output = "" output = ""
err = errors.New("timeout") err = errors.New("timeout")
} }
return return
} }
func getClient(sshConfig SSHConfig) (*ssh.Client, error) { func getClient(sshConfig SSHConfig) (*ssh.Client, error) {
config, err := parseSSHConfig(sshConfig) config, err := parseSSHConfig(sshConfig)
if err != nil { if err != nil {
return nil, err return nil, err
} }
addr := fmt.Sprintf("%s:%d", sshConfig.Host, sshConfig.Port) 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) {
func triggerTimeout(ch chan bool, timeout int){ // 最长执行时间不能超过24小时
// 最长执行时间不能超过24小时 if timeout <= 0 || timeout > 86400 {
if timeout <= 0 || timeout > 86400 { timeout = 86400
timeout = 86400 }
} time.Sleep(time.Duration(timeout) * time.Second)
time.Sleep(time.Duration(timeout) * time.Second) close(ch)
close(ch)
} }

View File

@ -1,16 +1,16 @@
package utils package utils
import ( import (
"encoding/json" "encoding/json"
"github.com/ouqiang/gocron/modules/logger" "github.com/ouqiang/gocron/modules/logger"
) )
// json 格式输出 // json 格式输出
type response struct { type response struct {
Code int `json:"code"` // 状态码 0:成功 非0:失败 Code int `json:"code"` // 状态码 0:成功 非0:失败
Message string `json:"message"` // 信息 Message string `json:"message"` // 信息
Data interface{} `json:"data"` // 数据 Data interface{} `json:"data"` // 数据
} }
type JsonResponse struct{} type JsonResponse struct{}
@ -26,40 +26,40 @@ const SuccessContent = "操作成功"
const FailureContent = "操作失败" const FailureContent = "操作失败"
func JsonResponseByErr(err error) string { func JsonResponseByErr(err error) string {
json := JsonResponse{} json := JsonResponse{}
if err != nil { if err != nil {
return json.CommonFailure(FailureContent, err) return json.CommonFailure(FailureContent, err)
} }
return json.Success(SuccessContent, nil) return json.Success(SuccessContent, nil)
} }
func (j *JsonResponse) Success(message string, data interface{}) string { 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 { 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 { func (j *JsonResponse) CommonFailure(message string, err ...error) string {
if len(err) > 0 { if len(err) > 0 {
logger.Warn(err) logger.Warn(err)
} }
return j.Failure(ResponseFailure, message) return j.Failure(ResponseFailure, message)
} }
func (j *JsonResponse) response(code int, message string, data interface{}) string { func (j *JsonResponse) response(code int, message string, data interface{}) string {
resp := response{ resp := response{
Code: code, Code: code,
Message: message, Message: message,
Data: data, Data: data,
} }
result, err := json.Marshal(resp) result, err := json.Marshal(resp)
if err != nil { if err != nil {
logger.Error(err) logger.Error(err)
} }
return string(result) return string(result)
} }

View File

@ -1,107 +1,107 @@
package utils package utils
import ( import (
"crypto/md5" "crypto/md5"
"encoding/hex" "encoding/hex"
"math/rand" "fmt"
"time" "github.com/Tang-RoseChild/mahonia"
"runtime" "math/rand"
"github.com/Tang-RoseChild/mahonia" "os"
"strings" "runtime"
"os" "strings"
"fmt" "time"
) )
// 生成长度为length的随机字符串 // 生成长度为length的随机字符串
func RandString(length int64) string { func RandString(length int64) string {
sources := []byte("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") sources := []byte("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
result := []byte{} result := []byte{}
r := rand.New(rand.NewSource(time.Now().UnixNano())) r := rand.New(rand.NewSource(time.Now().UnixNano()))
sourceLength := len(sources) sourceLength := len(sources)
var i int64 = 0 var i int64 = 0
for ; i < length; i++ { for ; i < length; i++ {
result = append(result, sources[r.Intn(sourceLength)]) result = append(result, sources[r.Intn(sourceLength)])
} }
return string(result) return string(result)
} }
// 生成32位MD5摘要 // 生成32位MD5摘要
func Md5(str string) string { func Md5(str string) string {
m := md5.New() m := md5.New()
m.Write([]byte(str)) m.Write([]byte(str))
return hex.EncodeToString(m.Sum(nil)) return hex.EncodeToString(m.Sum(nil))
} }
// 生成0-max之间随机数 // 生成0-max之间随机数
func RandNumber(max int) int { 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 // 判断当前系统是否是windows
func IsWindows() bool { func IsWindows() bool {
return runtime.GOOS == "windows" return runtime.GOOS == "windows"
} }
// GBK编码转换为UTF8 // GBK编码转换为UTF8
func GBK2UTF8(s string) (string, bool) { 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 { func ReplaceStrings(s string, old []string, replace []string) string {
if s == "" { if s == "" {
return s return s
} }
if len(old) != len(replace) { if len(old) != len(replace) {
return s return s
} }
for i, v := range old { for i, v := range old {
s = strings.Replace(s, v, replace[i], 1000) s = strings.Replace(s, v, replace[i], 1000)
} }
return s return s
} }
func InStringSlice(slice []string, element string) bool { func InStringSlice(slice []string, element string) bool {
element = strings.TrimSpace(element) element = strings.TrimSpace(element)
for _, v := range slice { for _, v := range slice {
if strings.TrimSpace(v) == element{ if strings.TrimSpace(v) == element {
return true return true
} }
} }
return false return false
} }
// 转义json特殊字符 // 转义json特殊字符
func EscapeJson(s string) string { func EscapeJson(s string) string {
specialChars := []string{"\\", "\b","\f", "\n", "\r", "\t", "\"",} specialChars := []string{"\\", "\b", "\f", "\n", "\r", "\t", "\""}
replaceChars := []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 { func FileExist(file string) bool {
_, err := os.Stat(file) _, err := os.Stat(file)
if os.IsNotExist(err) { if os.IsNotExist(err) {
return false return false
} }
if os.IsPermission(err) { if os.IsPermission(err) {
return false return false
} }
return true return true
} }
// 格式化环境变量 // 格式化环境变量
func FormatUnixEnv(key, value string) string { func FormatUnixEnv(key, value string) string {
return fmt.Sprintf("export %s=%s; ", key, value) return fmt.Sprintf("export %s=%s; ", key, value)
} }

View File

@ -3,22 +3,22 @@ package utils
import "testing" import "testing"
func TestRandString(t *testing.T) { func TestRandString(t *testing.T) {
str := RandString(32) str := RandString(32)
if len(str) != 32 { if len(str) != 32 {
t.Fatalf("长度不匹配,目标长度32, 实际%d-%s", len(str), str) t.Fatalf("长度不匹配,目标长度32, 实际%d-%s", len(str), str)
} }
} }
func TestMd5(t *testing.T) { func TestMd5(t *testing.T) {
str := Md5("123456") str := Md5("123456")
if len(str) != 32 { if len(str) != 32 {
t.Fatalf("长度不匹配,目标长度32, 实际%d-%s", len(str), str) t.Fatalf("长度不匹配,目标长度32, 实际%d-%s", len(str), str)
} }
} }
func TestRandNumber(t *testing.T) { func TestRandNumber(t *testing.T) {
num := RandNumber(10000) num := RandNumber(10000)
if num <= 0 && num >= 10000 { if num <= 0 && num >= 10000 {
t.Fatalf("随机数不在有效范围内-%d", num) t.Fatalf("随机数不在有效范围内-%d", num)
} }
} }

View File

@ -3,35 +3,35 @@
package utils package utils
import ( import (
"os/exec" "errors"
"syscall" "golang.org/x/net/context"
"golang.org/x/net/context" "os/exec"
"errors" "syscall"
) )
type Result struct { type Result struct {
output string output string
err error err error
} }
// 执行shell命令可设置执行超时时间 // 执行shell命令可设置执行超时时间
func ExecShell(ctx context.Context, command string) (string, error) { func ExecShell(ctx context.Context, command string) (string, error) {
cmd := exec.Command("/bin/bash", "-c", command) cmd := exec.Command("/bin/bash", "-c", command)
cmd.SysProcAttr = &syscall.SysProcAttr{ cmd.SysProcAttr = &syscall.SysProcAttr{
Setpgid: true, Setpgid: true,
} }
var resultChan chan Result = make(chan Result) var resultChan chan Result = make(chan Result)
go func() { go func() {
output ,err := cmd.CombinedOutput() output, err := cmd.CombinedOutput()
resultChan <- Result{string(output), err} resultChan <- Result{string(output), err}
}() }()
select { select {
case <- ctx.Done(): case <-ctx.Done():
if cmd.Process.Pid > 0 { if cmd.Process.Pid > 0 {
syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL) syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
} }
return "", errors.New("timeout killed") return "", errors.New("timeout killed")
case result := <- resultChan: case result := <-resultChan:
return result.output, result.err return result.output, result.err
} }
} }

View File

@ -3,51 +3,48 @@
package utils package utils
import ( import (
"syscall" "errors"
"os/exec" "golang.org/x/net/context"
"strconv" "os/exec"
"golang.org/x/net/context" "strconv"
"errors" "syscall"
) )
type Result struct { type Result struct {
output string output string
err error err error
} }
// 执行shell命令可设置执行超时时间 // 执行shell命令可设置执行超时时间
func ExecShell(ctx context.Context, command string) (string, error) { func ExecShell(ctx context.Context, command string) (string, error) {
cmd := exec.Command("cmd", "/C", command) cmd := exec.Command("cmd", "/C", command)
// 隐藏cmd窗口 // 隐藏cmd窗口
cmd.SysProcAttr = &syscall.SysProcAttr{ cmd.SysProcAttr = &syscall.SysProcAttr{
HideWindow: true, HideWindow: true,
} }
var resultChan chan Result = make(chan Result) var resultChan chan Result = make(chan Result)
go func() { go func() {
output ,err := cmd.CombinedOutput() output, err := cmd.CombinedOutput()
resultChan <- Result{string(output), err} resultChan <- Result{string(output), err}
}() }()
select { select {
case <- ctx.Done(): case <-ctx.Done():
if cmd.Process.Pid > 0 { if cmd.Process.Pid > 0 {
exec.Command("taskkill", "/F", "/T", "/PID", strconv.Itoa(cmd.Process.Pid)).Run() exec.Command("taskkill", "/F", "/T", "/PID", strconv.Itoa(cmd.Process.Pid)).Run()
cmd.Process.Kill() cmd.Process.Kill()
} }
return "", errors.New("timeout killed") return "", errors.New("timeout killed")
case result := <- resultChan: case result := <-resultChan:
return ConvertEncoding(result.output), result.err return ConvertEncoding(result.output), result.err
} }
return "", nil
} }
func ConvertEncoding(outputGBK string) (string) { func ConvertEncoding(outputGBK string) string {
// windows平台编码为gbk需转换为utf8才能入库 // windows平台编码为gbk需转换为utf8才能入库
outputUTF8, ok := GBK2UTF8(outputGBK) outputUTF8, ok := GBK2UTF8(outputGBK)
if ok { if ok {
return outputUTF8 return outputUTF8
} }
return "命令输出转换编码失败(gbk to utf8)" return "命令输出转换编码失败(gbk to utf8)"
} }

View File

@ -1,20 +1,20 @@
package base package base
import ( 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) { func ParsePageAndPageSize(ctx *macaron.Context, params models.CommonMap) {
page := ctx.QueryInt("page") page := ctx.QueryInt("page")
pageSize := ctx.QueryInt("page_size") pageSize := ctx.QueryInt("page_size")
if page <= 0 { if page <= 0 {
page = 1 page = 1
} }
if pageSize <= 0 { if pageSize <= 0 {
pageSize = models.PageSize pageSize = models.PageSize
} }
params["Page"] = page params["Page"] = page
params["PageSize"] = pageSize params["PageSize"] = pageSize
} }

View File

@ -3,6 +3,6 @@ package routers
import "gopkg.in/macaron.v1" import "gopkg.in/macaron.v1"
// 首页 // 首页
func Home(ctx *macaron.Context) { func Home(ctx *macaron.Context) {
ctx.Redirect("/task") ctx.Redirect("/task")
} }

View File

@ -1,194 +1,192 @@
package host package host
import ( import (
"gopkg.in/macaron.v1" "fmt"
"github.com/ouqiang/gocron/models" "github.com/Unknwon/paginater"
"github.com/ouqiang/gocron/modules/utils" "github.com/go-macaron/binding"
"github.com/ouqiang/gocron/modules/logger" "github.com/ouqiang/gocron/models"
"strconv" "github.com/ouqiang/gocron/modules/logger"
"github.com/ouqiang/gocron/service" "github.com/ouqiang/gocron/modules/rpc/client"
"github.com/Unknwon/paginater" "github.com/ouqiang/gocron/modules/rpc/grpcpool"
"fmt" "github.com/ouqiang/gocron/modules/rpc/proto"
"html/template" "github.com/ouqiang/gocron/modules/utils"
"github.com/ouqiang/gocron/routers/base" "github.com/ouqiang/gocron/routers/base"
"github.com/go-macaron/binding" "github.com/ouqiang/gocron/service"
"github.com/ouqiang/gocron/modules/rpc/grpcpool" "gopkg.in/macaron.v1"
"strings" "html/template"
"github.com/ouqiang/gocron/modules/rpc/client" "strconv"
"github.com/ouqiang/gocron/modules/rpc/proto" "strings"
) )
func Index(ctx *macaron.Context) { func Index(ctx *macaron.Context) {
hostModel := new(models.Host) hostModel := new(models.Host)
queryParams := parseQueryParams(ctx) queryParams := parseQueryParams(ctx)
total, err := hostModel.Total(queryParams) total, err := hostModel.Total(queryParams)
hosts, err := hostModel.List(queryParams) hosts, err := hostModel.List(queryParams)
if err != nil { if err != nil {
logger.Error(err) logger.Error(err)
} }
name, ok := queryParams["name"].(string) name, ok := queryParams["name"].(string)
var safeNameHTML = "" var safeNameHTML = ""
if ok { if ok {
safeNameHTML = template.HTMLEscapeString(name) safeNameHTML = template.HTMLEscapeString(name)
} }
PageParams := fmt.Sprintf("id=%d&name=%s&page_size=%d", PageParams := fmt.Sprintf("id=%d&name=%s&page_size=%d",
queryParams["Id"], safeNameHTML, queryParams["PageSize"]); queryParams["Id"], safeNameHTML, queryParams["PageSize"])
queryParams["PageParams"] = template.URL(PageParams) queryParams["PageParams"] = template.URL(PageParams)
p := paginater.New(int(total), queryParams["PageSize"].(int), queryParams["Page"].(int), 5) p := paginater.New(int(total), queryParams["PageSize"].(int), queryParams["Page"].(int), 5)
ctx.Data["Pagination"] = p ctx.Data["Pagination"] = p
ctx.Data["Title"] = "主机列表" ctx.Data["Title"] = "主机列表"
ctx.Data["Hosts"] = hosts ctx.Data["Hosts"] = hosts
ctx.Data["Params"] = queryParams ctx.Data["Params"] = queryParams
ctx.HTML(200, "host/index") ctx.HTML(200, "host/index")
} }
func Create(ctx *macaron.Context) { func Create(ctx *macaron.Context) {
ctx.Data["Title"] = "添加主机" ctx.Data["Title"] = "添加主机"
ctx.HTML(200, "host/host_form") ctx.HTML(200, "host/host_form")
} }
func Edit(ctx *macaron.Context) { func Edit(ctx *macaron.Context) {
ctx.Data["Title"] = "编辑主机" ctx.Data["Title"] = "编辑主机"
hostModel := new(models.Host) hostModel := new(models.Host)
id := ctx.ParamsInt(":id") id := ctx.ParamsInt(":id")
err := hostModel.Find(id) err := hostModel.Find(id)
if err != nil { if err != nil {
logger.Errorf("获取主机详情失败#主机id-%d", id) logger.Errorf("获取主机详情失败#主机id-%d", id)
} }
ctx.Data["Host"] = hostModel ctx.Data["Host"] = hostModel
ctx.HTML(200, "host/host_form") ctx.HTML(200, "host/host_form")
} }
type HostForm struct { type HostForm struct {
Id int16 Id int16
Name string `binding:"Required;MaxSize(64)"` Name string `binding:"Required;MaxSize(64)"`
Alias string `binding:"Required;MaxSize(32)"` Alias string `binding:"Required;MaxSize(32)"`
Port int `binding:"Required;Range(1-65535)"` Port int `binding:"Required;Range(1-65535)"`
Remark string Remark string
} }
func (f HostForm) Error(ctx *macaron.Context, errs binding.Errors) { func (f HostForm) Error(ctx *macaron.Context, errs binding.Errors) {
if len(errs) == 0 { if len(errs) == 0 {
return return
} }
json := utils.JsonResponse{} json := utils.JsonResponse{}
content := json.CommonFailure("表单验证失败, 请检测输入") content := json.CommonFailure("表单验证失败, 请检测输入")
ctx.Resp.Write([]byte(content)) ctx.Resp.Write([]byte(content))
} }
func Store(ctx *macaron.Context, form HostForm) string { func Store(ctx *macaron.Context, form HostForm) string {
json := utils.JsonResponse{} json := utils.JsonResponse{}
hostModel := new(models.Host) hostModel := new(models.Host)
id := form.Id id := form.Id
nameExist, err := hostModel.NameExists(form.Name, form.Id) nameExist, err := hostModel.NameExists(form.Name, form.Id)
if err != nil { if err != nil {
return json.CommonFailure("操作失败", err) return json.CommonFailure("操作失败", err)
} }
if nameExist { if nameExist {
return json.CommonFailure("主机名已存在") return json.CommonFailure("主机名已存在")
} }
hostModel.Name = strings.TrimSpace(form.Name) hostModel.Name = strings.TrimSpace(form.Name)
hostModel.Alias = strings.TrimSpace(form.Alias) hostModel.Alias = strings.TrimSpace(form.Alias)
hostModel.Port = form.Port hostModel.Port = form.Port
hostModel.Remark = strings.TrimSpace(form.Remark) hostModel.Remark = strings.TrimSpace(form.Remark)
isCreate := false isCreate := false
oldHostModel := new(models.Host) oldHostModel := new(models.Host)
err = oldHostModel.Find(int(id)) err = oldHostModel.Find(int(id))
if err != nil { if err != nil {
return json.CommonFailure("主机不存在") return json.CommonFailure("主机不存在")
} }
if id > 0 { if id > 0 {
_, err = hostModel.UpdateBean(id) _, err = hostModel.UpdateBean(id)
} else { } else {
isCreate = true isCreate = true
id, err = hostModel.Create() id, err = hostModel.Create()
} }
if err != nil { if err != nil {
return json.CommonFailure("保存失败", err) return json.CommonFailure("保存失败", err)
} }
if !isCreate { if !isCreate {
oldAddr := fmt.Sprintf("%s:%d", oldHostModel.Name, oldHostModel.Port) oldAddr := fmt.Sprintf("%s:%d", oldHostModel.Name, oldHostModel.Port)
newAddr := fmt.Sprintf("%s:%d", hostModel.Name, hostModel.Port) newAddr := fmt.Sprintf("%s:%d", hostModel.Name, hostModel.Port)
if oldAddr != newAddr { if oldAddr != newAddr {
grpcpool.Pool.Release(oldAddr) grpcpool.Pool.Release(oldAddr)
} }
taskModel := new(models.Task) taskModel := new(models.Task)
tasks, err := taskModel.ActiveListByHostId(id) tasks, err := taskModel.ActiveListByHostId(id)
if err != nil { if err != nil {
return json.CommonFailure("刷新任务主机信息失败", err) return json.CommonFailure("刷新任务主机信息失败", err)
} }
serviceTask := new(service.Task) serviceTask := new(service.Task)
serviceTask.BatchAdd(tasks) serviceTask.BatchAdd(tasks)
} }
return json.Success("保存成功", nil) return json.Success("保存成功", nil)
} }
func Remove(ctx *macaron.Context) string { func Remove(ctx *macaron.Context) string {
id, err := strconv.Atoi(ctx.Params(":id")) id, err := strconv.Atoi(ctx.Params(":id"))
json := utils.JsonResponse{} json := utils.JsonResponse{}
if err != nil { if err != nil {
return json.CommonFailure("参数错误", err) return json.CommonFailure("参数错误", err)
} }
taskHostModel := new(models.TaskHost) taskHostModel := new(models.TaskHost)
exist,err := taskHostModel.HostIdExist(int16(id)) exist, err := taskHostModel.HostIdExist(int16(id))
if err != nil { if err != nil {
return json.CommonFailure("操作失败", err) return json.CommonFailure("操作失败", err)
} }
if exist { if exist {
return json.CommonFailure("有任务引用此主机,不能删除") return json.CommonFailure("有任务引用此主机,不能删除")
} }
hostModel := new(models.Host) hostModel := new(models.Host)
err = hostModel.Find(int(id)) err = hostModel.Find(int(id))
if err != nil { if err != nil {
return json.CommonFailure("主机不存在") return json.CommonFailure("主机不存在")
} }
_, err =hostModel.Delete(id) _, err = hostModel.Delete(id)
if err != nil { if err != nil {
return json.CommonFailure("操作失败", err) return json.CommonFailure("操作失败", err)
} }
addr := fmt.Sprintf("%s:%d", hostModel.Name, hostModel.Port) addr := fmt.Sprintf("%s:%d", hostModel.Name, hostModel.Port)
grpcpool.Pool.Release(addr) grpcpool.Pool.Release(addr)
return json.Success("操作成功", nil)
return json.Success("操作成功", nil)
} }
func Ping(ctx *macaron.Context) string { func Ping(ctx *macaron.Context) string {
id := ctx.ParamsInt(":id") id := ctx.ParamsInt(":id")
hostModel := new(models.Host) hostModel := new(models.Host)
err := hostModel.Find(id) err := hostModel.Find(id)
json := utils.JsonResponse{} json := utils.JsonResponse{}
if err != nil || hostModel.Id <= 0{ if err != nil || hostModel.Id <= 0 {
return json.CommonFailure("主机不存在", err) 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{} return json.Success("连接成功", nil)
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)
} }
// 解析查询参数 // 解析查询参数
func parseQueryParams(ctx *macaron.Context) (models.CommonMap) { func parseQueryParams(ctx *macaron.Context) models.CommonMap {
var params models.CommonMap = models.CommonMap{} var params models.CommonMap = models.CommonMap{}
params["Id"] = ctx.QueryInt("id") params["Id"] = ctx.QueryInt("id")
params["Name"] = ctx.QueryTrim("name") params["Name"] = ctx.QueryTrim("name")
base.ParsePageAndPageSize(ctx, params) base.ParsePageAndPageSize(ctx, params)
return params return params
} }

View File

@ -1,163 +1,163 @@
package install package install
import ( import (
"github.com/ouqiang/gocron/models" "fmt"
"github.com/ouqiang/gocron/modules/app" "github.com/go-macaron/binding"
"github.com/ouqiang/gocron/modules/setting" "github.com/ouqiang/gocron/models"
"github.com/ouqiang/gocron/modules/utils" "github.com/ouqiang/gocron/modules/app"
"gopkg.in/macaron.v1" "github.com/ouqiang/gocron/modules/setting"
"strconv" "github.com/ouqiang/gocron/modules/utils"
"fmt" "github.com/ouqiang/gocron/service"
"github.com/ouqiang/gocron/service" "gopkg.in/macaron.v1"
"github.com/go-macaron/binding" "strconv"
) )
// 系统安装 // 系统安装
type InstallForm struct { type InstallForm struct {
DbType string `binding:"In(mysql)"` DbType string `binding:"In(mysql)"`
DbHost string `binding:"Required;MaxSize(50)"` DbHost string `binding:"Required;MaxSize(50)"`
DbPort int `binding:"Required;Range(1,65535)"` DbPort int `binding:"Required;Range(1,65535)"`
DbUsername string `binding:"Required;MaxSize(50)"` DbUsername string `binding:"Required;MaxSize(50)"`
DbPassword string `binding:"Required;MaxSize(30)"` DbPassword string `binding:"Required;MaxSize(30)"`
DbName string `binding:"Required;MaxSize(50)"` DbName string `binding:"Required;MaxSize(50)"`
DbTablePrefix string `binding:"MaxSize(20)"` DbTablePrefix string `binding:"MaxSize(20)"`
AdminUsername string `binding:"Required;MinSize(3)"` AdminUsername string `binding:"Required;MinSize(3)"`
AdminPassword string `binding:"Required;MinSize(6)"` AdminPassword string `binding:"Required;MinSize(6)"`
ConfirmAdminPassword string `binding:"Required;MinSize(6)"` ConfirmAdminPassword string `binding:"Required;MinSize(6)"`
AdminEmail string `binding:"Required;Email;MaxSize(50)"` AdminEmail string `binding:"Required;Email;MaxSize(50)"`
} }
func (f InstallForm) Error(ctx *macaron.Context, errs binding.Errors) { func (f InstallForm) Error(ctx *macaron.Context, errs binding.Errors) {
if len(errs) == 0 { if len(errs) == 0 {
return return
} }
json := utils.JsonResponse{} json := utils.JsonResponse{}
content := json.CommonFailure("表单验证失败, 请检测输入") content := json.CommonFailure("表单验证失败, 请检测输入")
ctx.Resp.Write([]byte(content)) ctx.Resp.Write([]byte(content))
} }
func Create(ctx *macaron.Context) { func Create(ctx *macaron.Context) {
if app.Installed { if app.Installed {
ctx.Redirect("/") ctx.Redirect("/")
} }
ctx.Data["Title"] = "安装" ctx.Data["Title"] = "安装"
ctx.Data["DisableNav"] = true ctx.Data["DisableNav"] = true
ctx.HTML(200, "install/create") ctx.HTML(200, "install/create")
} }
// 安装 // 安装
func Store(ctx *macaron.Context, form InstallForm) string { func Store(ctx *macaron.Context, form InstallForm) string {
json := utils.JsonResponse{} json := utils.JsonResponse{}
if app.Installed { if app.Installed {
return json.CommonFailure("系统已安装!") return json.CommonFailure("系统已安装!")
} }
if form.AdminPassword != form.ConfirmAdminPassword { if form.AdminPassword != form.ConfirmAdminPassword {
return json.CommonFailure("两次输入密码不匹配") return json.CommonFailure("两次输入密码不匹配")
} }
err := testDbConnection(form) err := testDbConnection(form)
if err != nil { if err != nil {
return json.CommonFailure("数据库连接失败", err) return json.CommonFailure("数据库连接失败", err)
} }
// 写入数据库配置 // 写入数据库配置
err = writeConfig(form) err = writeConfig(form)
if err != nil { if err != nil {
return json.CommonFailure("数据库配置写入文件失败", err) return json.CommonFailure("数据库配置写入文件失败", err)
} }
appConfig, err := setting.Read(app.AppConfig) appConfig, err := setting.Read(app.AppConfig)
if err != nil { if err != nil {
return json.CommonFailure("读取应用配置失败", err) return json.CommonFailure("读取应用配置失败", err)
} }
app.Setting = appConfig app.Setting = appConfig
models.Db = models.CreateDb() models.Db = models.CreateDb()
// 创建数据库表 // 创建数据库表
migration := new(models.Migration) migration := new(models.Migration)
err = migration.Install(form.DbName) err = migration.Install(form.DbName)
if err != nil { if err != nil {
return json.CommonFailure(fmt.Sprintf("创建数据库表失败-%s", err.Error()), err) return json.CommonFailure(fmt.Sprintf("创建数据库表失败-%s", err.Error()), err)
} }
// 创建管理员账号 // 创建管理员账号
err = createAdminUser(form) err = createAdminUser(form)
if err != nil { if err != nil {
return json.CommonFailure("创建管理员账号失败", err) return json.CommonFailure("创建管理员账号失败", err)
} }
// 创建安装锁 // 创建安装锁
err = app.CreateInstallLock() err = app.CreateInstallLock()
if err != nil { if err != nil {
return json.CommonFailure("创建文件安装锁失败", err) return json.CommonFailure("创建文件安装锁失败", err)
} }
// 更新版本号文件 // 更新版本号文件
app.UpdateVersionFile() app.UpdateVersionFile()
app.Installed = true app.Installed = true
// 初始化定时任务 // 初始化定时任务
serviceTask := new(service.Task) serviceTask := new(service.Task)
serviceTask.Initialize() serviceTask.Initialize()
return json.Success("安装成功", nil) return json.Success("安装成功", nil)
} }
// 配置写入文件 // 配置写入文件
func writeConfig(form InstallForm) error { func writeConfig(form InstallForm) error {
dbConfig := []string{ dbConfig := []string{
"db.engine", form.DbType, "db.engine", form.DbType,
"db.host", form.DbHost, "db.host", form.DbHost,
"db.port", strconv.Itoa(form.DbPort), "db.port", strconv.Itoa(form.DbPort),
"db.user", form.DbUsername, "db.user", form.DbUsername,
"db.password",form.DbPassword, "db.password", form.DbPassword,
"db.database", form.DbName, "db.database", form.DbName,
"db.prefix", form.DbTablePrefix, "db.prefix", form.DbTablePrefix,
"db.charset", "utf8", "db.charset", "utf8",
"db.max.idle.conns", "30", "db.max.idle.conns", "30",
"db.max.open.conns", "100", "db.max.open.conns", "100",
"allow_ips", "", "allow_ips", "",
"app.name", "定时任务管理系统", // 应用名称 "app.name", "定时任务管理系统", // 应用名称
"api.key", "", "api.key", "",
"api.secret", "", "api.secret", "",
"enable_tls", "false", "enable_tls", "false",
"ca_file", "", "ca_file", "",
"cert_file", "", "cert_file", "",
"key_file", "", "key_file", "",
} }
return setting.Write(dbConfig, app.AppConfig) return setting.Write(dbConfig, app.AppConfig)
} }
// 创建管理员账号 // 创建管理员账号
func createAdminUser(form InstallForm) error { func createAdminUser(form InstallForm) error {
user := new(models.User) user := new(models.User)
user.Name = form.AdminUsername user.Name = form.AdminUsername
user.Password = form.AdminPassword user.Password = form.AdminPassword
user.Email = form.AdminEmail user.Email = form.AdminEmail
user.IsAdmin = 1 user.IsAdmin = 1
_, err := user.Create() _, err := user.Create()
return err return err
} }
// 测试数据库连接 // 测试数据库连接
func testDbConnection(form InstallForm) error { func testDbConnection(form InstallForm) error {
var s setting.Setting var s setting.Setting
s.Db.Engine = form.DbType s.Db.Engine = form.DbType
s.Db.Host = form.DbHost s.Db.Host = form.DbHost
s.Db.Port = form.DbPort s.Db.Port = form.DbPort
s.Db.User = form.DbUsername s.Db.User = form.DbUsername
s.Db.Password = form.DbPassword s.Db.Password = form.DbPassword
s.Db.Charset = "utf8" s.Db.Charset = "utf8"
db, err := models.CreateTmpDb(&s) db, err := models.CreateTmpDb(&s)
if err != nil { if err != nil {
return err return err
} }
defer db.Close() defer db.Close()
err = db.Ping() err = db.Ping()
return err return err
} }

View File

@ -1,30 +1,30 @@
package loginlog package loginlog
import ( import (
"gopkg.in/macaron.v1" "fmt"
"github.com/Unknwon/paginater" "github.com/Unknwon/paginater"
"fmt" "github.com/ouqiang/gocron/models"
"github.com/ouqiang/gocron/modules/logger" "github.com/ouqiang/gocron/modules/logger"
"github.com/ouqiang/gocron/models" "github.com/ouqiang/gocron/routers/base"
"github.com/ouqiang/gocron/routers/base" "gopkg.in/macaron.v1"
"html/template" "html/template"
) )
func Index(ctx *macaron.Context) { func Index(ctx *macaron.Context) {
loginLogModel := new(models.LoginLog) loginLogModel := new(models.LoginLog)
params := models.CommonMap{} params := models.CommonMap{}
base.ParsePageAndPageSize(ctx, params) base.ParsePageAndPageSize(ctx, params)
total, err := loginLogModel.Total() total, err := loginLogModel.Total()
loginLogs, err := loginLogModel.List(params) loginLogs, err := loginLogModel.List(params)
if err != nil { if err != nil {
logger.Error(err) logger.Error(err)
} }
PageParams := fmt.Sprintf("page_size=%d", params["PageSize"]); PageParams := fmt.Sprintf("page_size=%d", params["PageSize"])
params["PageParams"] = template.URL(PageParams) params["PageParams"] = template.URL(PageParams)
p := paginater.New(int(total), params["PageSize"].(int), params["Page"].(int), 5) p := paginater.New(int(total), params["PageSize"].(int), params["Page"].(int), 5)
ctx.Data["Pagination"] = p ctx.Data["Pagination"] = p
ctx.Data["Title"] = "登录日志" ctx.Data["Title"] = "登录日志"
ctx.Data["LoginLogs"] = loginLogs ctx.Data["LoginLogs"] = loginLogs
ctx.Data["Params"] = params ctx.Data["Params"] = params
ctx.HTML(200, "manage/login_log") ctx.HTML(200, "manage/login_log")
} }

View File

@ -1,138 +1,136 @@
package manage package manage
import ( import (
"gopkg.in/macaron.v1" "encoding/json"
"github.com/ouqiang/gocron/modules/utils" "github.com/ouqiang/gocron/models"
"github.com/ouqiang/gocron/models" "github.com/ouqiang/gocron/modules/logger"
"github.com/ouqiang/gocron/modules/logger" "github.com/ouqiang/gocron/modules/utils"
"encoding/json" "gopkg.in/macaron.v1"
) )
// region slack // region slack
func EditSlack(ctx *macaron.Context) { func EditSlack(ctx *macaron.Context) {
ctx.Data["Title"] = "Slack配置" ctx.Data["Title"] = "Slack配置"
settingModel := new(models.Setting) settingModel := new(models.Setting)
slack, err := settingModel.Slack() slack, err := settingModel.Slack()
if err != nil { if err != nil {
logger.Error(err) logger.Error(err)
} }
ctx.Data["Slack"] = slack ctx.Data["Slack"] = slack
ctx.HTML(200, "manage/slack") ctx.HTML(200, "manage/slack")
} }
func Slack(ctx *macaron.Context) string { func Slack(ctx *macaron.Context) string {
settingModel := new(models.Setting) settingModel := new(models.Setting)
slack, err := settingModel.Slack() slack, err := settingModel.Slack()
if err != nil { if err != nil {
logger.Error(err) logger.Error(err)
} }
json := utils.JsonResponse{} json := utils.JsonResponse{}
return json.Success("", slack) return json.Success("", slack)
} }
func UpdateSlackUrl(ctx *macaron.Context) string { func UpdateSlackUrl(ctx *macaron.Context) string {
url := ctx.QueryTrim("url") url := ctx.QueryTrim("url")
settingModel := new(models.Setting) settingModel := new(models.Setting)
_, err := settingModel.UpdateSlackUrl(url) _, err := settingModel.UpdateSlackUrl(url)
return utils.JsonResponseByErr(err) return utils.JsonResponseByErr(err)
} }
func CreateSlackChannel(ctx *macaron.Context) string { func CreateSlackChannel(ctx *macaron.Context) string {
channel := ctx.QueryTrim("channel") channel := ctx.QueryTrim("channel")
settingModel := new(models.Setting) settingModel := new(models.Setting)
if settingModel.IsChannelExist(channel) { if settingModel.IsChannelExist(channel) {
json := utils.JsonResponse{} json := utils.JsonResponse{}
return json.CommonFailure("Channel已存在") return json.CommonFailure("Channel已存在")
} }
_, err := settingModel.CreateChannel(channel) _, err := settingModel.CreateChannel(channel)
return utils.JsonResponseByErr(err) return utils.JsonResponseByErr(err)
} }
func RemoveSlackChannel(ctx *macaron.Context) string { func RemoveSlackChannel(ctx *macaron.Context) string {
id := ctx.ParamsInt(":id") id := ctx.ParamsInt(":id")
settingModel := new(models.Setting) settingModel := new(models.Setting)
_, err := settingModel.RemoveChannel(id) _, err := settingModel.RemoveChannel(id)
return utils.JsonResponseByErr(err) return utils.JsonResponseByErr(err)
} }
// endregion // endregion
// region 邮件 // region 邮件
func EditMail(ctx *macaron.Context) { func EditMail(ctx *macaron.Context) {
ctx.Data["Title"] = "邮件配置" ctx.Data["Title"] = "邮件配置"
settingModel := new(models.Setting) settingModel := new(models.Setting)
mail, err := settingModel.Mail() mail, err := settingModel.Mail()
if err != nil { if err != nil {
logger.Error(err) logger.Error(err)
} }
ctx.Data["Mail"] = mail ctx.Data["Mail"] = mail
ctx.HTML(200, "manage/mail") ctx.HTML(200, "manage/mail")
} }
func Mail(ctx *macaron.Context) string { func Mail(ctx *macaron.Context) string {
settingModel := new(models.Setting) settingModel := new(models.Setting)
mail, err := settingModel.Mail() mail, err := settingModel.Mail()
if err != nil { if err != nil {
logger.Error(err) logger.Error(err)
} }
json := utils.JsonResponse{} json := utils.JsonResponse{}
return json.Success("", mail) return json.Success("", mail)
} }
type MailServerForm struct { type MailServerForm struct {
Host string `binding:"Required;MaxSize(100)"` Host string `binding:"Required;MaxSize(100)"`
Port int `binding:"Required;Range(1-65535)"` Port int `binding:"Required;Range(1-65535)"`
User string `binding:"Required;MaxSize(64);Email"` User string `binding:"Required;MaxSize(64);Email"`
Password string `binding:"Required;MaxSize(64)"` Password string `binding:"Required;MaxSize(64)"`
} }
func UpdateMailServer(ctx *macaron.Context, form MailServerForm) string { func UpdateMailServer(ctx *macaron.Context, form MailServerForm) string {
jsonByte, _ := json.Marshal(form) jsonByte, _ := json.Marshal(form)
settingModel := new(models.Setting) settingModel := new(models.Setting)
_, err := settingModel.UpdateMailServer(string(jsonByte)) _, err := settingModel.UpdateMailServer(string(jsonByte))
return utils.JsonResponseByErr(err) return utils.JsonResponseByErr(err)
} }
func ClearMailServer(ctx *macaron.Context) string { func ClearMailServer(ctx *macaron.Context) string {
jsonByte, _ := json.Marshal(MailServerForm{}) jsonByte, _ := json.Marshal(MailServerForm{})
settingModel := new(models.Setting) settingModel := new(models.Setting)
_, err := settingModel.UpdateMailServer(string(jsonByte)) _, err := settingModel.UpdateMailServer(string(jsonByte))
return utils.JsonResponseByErr(err) return utils.JsonResponseByErr(err)
} }
func CreateMailUser(ctx *macaron.Context) string { func CreateMailUser(ctx *macaron.Context) string {
username := ctx.QueryTrim("username") username := ctx.QueryTrim("username")
email := ctx.QueryTrim("email") email := ctx.QueryTrim("email")
settingModel := new(models.Setting) settingModel := new(models.Setting)
if username == "" || email == "" { if username == "" || email == "" {
json := utils.JsonResponse{} json := utils.JsonResponse{}
return json.CommonFailure("用户名、邮箱均不能为空") return json.CommonFailure("用户名、邮箱均不能为空")
} }
_, err := settingModel.CreateMailUser(username, email) _, err := settingModel.CreateMailUser(username, email)
return utils.JsonResponseByErr(err) return utils.JsonResponseByErr(err)
} }
func RemoveMailUser(ctx *macaron.Context) string { func RemoveMailUser(ctx *macaron.Context) string {
id := ctx.ParamsInt(":id") id := ctx.ParamsInt(":id")
settingModel := new(models.Setting) settingModel := new(models.Setting)
_, err := settingModel.RemoveMailUser(id) _, err := settingModel.RemoveMailUser(id)
return utils.JsonResponseByErr(err) return utils.JsonResponseByErr(err)
} }
// endregion // endregion

View File

@ -1,27 +1,27 @@
package routers package routers
import ( import (
"github.com/go-macaron/binding" "github.com/go-macaron/binding"
"github.com/ouqiang/gocron/routers/install" "github.com/go-macaron/cache"
"gopkg.in/macaron.v1" "github.com/go-macaron/captcha"
"github.com/ouqiang/gocron/routers/task" "github.com/go-macaron/gzip"
"github.com/ouqiang/gocron/routers/host" "github.com/go-macaron/session"
"github.com/ouqiang/gocron/routers/tasklog" "github.com/go-macaron/toolbox"
"github.com/ouqiang/gocron/modules/utils" "github.com/ouqiang/gocron/modules/app"
"github.com/go-macaron/session" "github.com/ouqiang/gocron/modules/logger"
"github.com/go-macaron/toolbox" "github.com/ouqiang/gocron/modules/utils"
"strings" "github.com/ouqiang/gocron/routers/host"
"github.com/ouqiang/gocron/modules/app" "github.com/ouqiang/gocron/routers/install"
"github.com/ouqiang/gocron/modules/logger" "github.com/ouqiang/gocron/routers/loginlog"
"github.com/ouqiang/gocron/routers/user" "github.com/ouqiang/gocron/routers/manage"
"github.com/go-macaron/gzip" "github.com/ouqiang/gocron/routers/task"
"github.com/ouqiang/gocron/routers/manage" "github.com/ouqiang/gocron/routers/tasklog"
"github.com/ouqiang/gocron/routers/loginlog" "github.com/ouqiang/gocron/routers/user"
"time" "gopkg.in/macaron.v1"
"strconv" "html/template"
"html/template" "strconv"
"github.com/go-macaron/cache" "strings"
"github.com/go-macaron/captcha" "time"
) )
// 静态文件目录 // 静态文件目录
@ -29,261 +29,261 @@ const StaticDir = "public"
// 路由注册 // 路由注册
func Register(m *macaron.Macaron) { func Register(m *macaron.Macaron) {
// 所有GET方法自动注册HEAD方法 // 所有GET方法自动注册HEAD方法
m.SetAutoHead(true) m.SetAutoHead(true)
// 首页 // 首页
m.Get("/", Home) m.Get("/", Home)
// 系统安装 // 系统安装
m.Group("/install", func() { m.Group("/install", func() {
m.Get("", install.Create) m.Get("", install.Create)
m.Post("/store", binding.Bind(install.InstallForm{}), install.Store) m.Post("/store", binding.Bind(install.InstallForm{}), install.Store)
}) })
// 用户 // 用户
m.Group("/user", func() { m.Group("/user", func() {
m.Get("/login", user.Login) m.Get("/login", user.Login)
m.Post("/login", user.ValidateLogin) m.Post("/login", user.ValidateLogin)
m.Get("/logout", user.Logout) m.Get("/logout", user.Logout)
m.Get("/editPassword", user.EditPassword) m.Get("/editPassword", user.EditPassword)
m.Post("/editPassword", user.UpdatePassword) m.Post("/editPassword", user.UpdatePassword)
}) })
// 定时任务 // 定时任务
m.Group("/task", func() { m.Group("/task", func() {
m.Get("/create", task.Create) m.Get("/create", task.Create)
m.Post("/store", binding.Bind(task.TaskForm{}), task.Store) m.Post("/store", binding.Bind(task.TaskForm{}), task.Store)
m.Get("/edit/:id", task.Edit) m.Get("/edit/:id", task.Edit)
m.Get("", task.Index) m.Get("", task.Index)
m.Get("/log", tasklog.Index) m.Get("/log", tasklog.Index)
m.Post("/log/clear", tasklog.Clear) m.Post("/log/clear", tasklog.Clear)
m.Post("/remove/:id", task.Remove) m.Post("/remove/:id", task.Remove)
m.Post("/enable/:id", task.Enable) m.Post("/enable/:id", task.Enable)
m.Post("/disable/:id", task.Disable) m.Post("/disable/:id", task.Disable)
m.Get("/run/:id", task.Run) m.Get("/run/:id", task.Run)
}) })
// 主机 // 主机
m.Group("/host", func() { m.Group("/host", func() {
m.Get("/create", host.Create) m.Get("/create", host.Create)
m.Get("/edit/:id", host.Edit) m.Get("/edit/:id", host.Edit)
m.Post("/store", binding.Bind(host.HostForm{}), host.Store) m.Post("/store", binding.Bind(host.HostForm{}), host.Store)
m.Get("", host.Index) m.Get("", host.Index)
m.Get("/ping/:id", host.Ping) m.Get("/ping/:id", host.Ping)
m.Post("/remove/:id", host.Remove) m.Post("/remove/:id", host.Remove)
}) })
// 管理 // 管理
m.Group("/manage", func() { m.Group("/manage", func() {
m.Group("/slack", func() { m.Group("/slack", func() {
m.Get("/", manage.Slack) m.Get("/", manage.Slack)
m.Get("/edit", manage.EditSlack) m.Get("/edit", manage.EditSlack)
m.Post("/url", manage.UpdateSlackUrl) m.Post("/url", manage.UpdateSlackUrl)
m.Post("/channel", manage.CreateSlackChannel) m.Post("/channel", manage.CreateSlackChannel)
m.Post("/channel/remove/:id", manage.RemoveSlackChannel) m.Post("/channel/remove/:id", manage.RemoveSlackChannel)
}) })
m.Group("/mail", func() { m.Group("/mail", func() {
m.Get("/", manage.Mail) m.Get("/", manage.Mail)
m.Get("/edit", manage.EditMail) m.Get("/edit", manage.EditMail)
m.Post("/server", binding.Bind(manage.MailServerForm{}), manage.UpdateMailServer) m.Post("/server", binding.Bind(manage.MailServerForm{}), manage.UpdateMailServer)
m.Post("/server/clear", manage.ClearMailServer) m.Post("/server/clear", manage.ClearMailServer)
m.Post("/user", manage.CreateMailUser) m.Post("/user", manage.CreateMailUser)
m.Post("/user/remove/:id", manage.RemoveMailUser) m.Post("/user/remove/:id", manage.RemoveMailUser)
}) })
m.Get("/login-log", loginlog.Index) m.Get("/login-log", loginlog.Index)
}) })
// API // API
m.Group("/api/v1", func() { m.Group("/api/v1", func() {
m.Post("/tasklog/remove/:id", tasklog.Remove) m.Post("/tasklog/remove/:id", tasklog.Remove)
m.Post("/task/enable/:id", task.Enable) m.Post("/task/enable/:id", task.Enable)
m.Post("/task/disable/:id", task.Disable) m.Post("/task/disable/:id", task.Disable)
}, apiAuth); }, apiAuth)
// 404错误 // 404错误
m.NotFound(func(ctx *macaron.Context) { m.NotFound(func(ctx *macaron.Context) {
if isGetRequest(ctx) && !isAjaxRequest(ctx) { if isGetRequest(ctx) && !isAjaxRequest(ctx) {
ctx.Data["Title"] = "404 - NOT FOUND" ctx.Data["Title"] = "404 - NOT FOUND"
ctx.HTML(404, "error/404") ctx.HTML(404, "error/404")
} else { } else {
json := utils.JsonResponse{} json := utils.JsonResponse{}
ctx.Resp.Write([]byte(json.Failure(utils.NotFound, "您访问的地址不存在"))) ctx.Resp.Write([]byte(json.Failure(utils.NotFound, "您访问的地址不存在")))
} }
}) })
// 50x错误 // 50x错误
m.InternalServerError(func(ctx *macaron.Context) { m.InternalServerError(func(ctx *macaron.Context) {
logger.Debug("500错误") logger.Debug("500错误")
if isGetRequest(ctx) && !isAjaxRequest(ctx) { if isGetRequest(ctx) && !isAjaxRequest(ctx) {
ctx.Data["Title"] = "500 - INTERNAL SERVER ERROR" ctx.Data["Title"] = "500 - INTERNAL SERVER ERROR"
ctx.HTML(500, "error/500") ctx.HTML(500, "error/500")
} else { } else {
json := utils.JsonResponse{} json := utils.JsonResponse{}
ctx.Resp.Write([]byte(json.Failure(utils.ServerError, "网站暂时无法访问,请稍后再试"))) ctx.Resp.Write([]byte(json.Failure(utils.ServerError, "网站暂时无法访问,请稍后再试")))
} }
}) })
} }
// 中间件注册 // 中间件注册
func RegisterMiddleware(m *macaron.Macaron) { func RegisterMiddleware(m *macaron.Macaron) {
m.Use(macaron.Logger()) m.Use(macaron.Logger())
m.Use(macaron.Recovery()) m.Use(macaron.Recovery())
if macaron.Env != macaron.DEV { if macaron.Env != macaron.DEV {
m.Use(gzip.Gziper()) m.Use(gzip.Gziper())
} }
m.Use(macaron.Static(StaticDir)) m.Use(macaron.Static(StaticDir))
m.Use(macaron.Renderer(macaron.RenderOptions{ m.Use(macaron.Renderer(macaron.RenderOptions{
Directory: "templates", Directory: "templates",
Extensions: []string{".html"}, Extensions: []string{".html"},
// 模板语法分隔符,默认为 ["{{", "}}"] // 模板语法分隔符,默认为 ["{{", "}}"]
Delims: macaron.Delims{"{{{", "}}}"}, Delims: macaron.Delims{"{{{", "}}}"},
// 追加的 Content-Type 头信息,默认为 "UTF-8" // 追加的 Content-Type 头信息,默认为 "UTF-8"
Charset: "UTF-8", Charset: "UTF-8",
// 渲染具有缩进格式的 JSON默认为不缩进 // 渲染具有缩进格式的 JSON默认为不缩进
IndentJSON: true, IndentJSON: true,
// 渲染具有缩进格式的 XML默认为不缩进 // 渲染具有缩进格式的 XML默认为不缩进
IndentXML: true, IndentXML: true,
Funcs: []template.FuncMap{map[string]interface{} { Funcs: []template.FuncMap{map[string]interface{}{
"HostFormat": func(index int) bool { "HostFormat": func(index int) bool {
return (index + 1) % 3 == 0 return (index+1)%3 == 0
}, },
"unescape": func(str string) template.HTML { "unescape": func(str string) template.HTML {
return template.HTML(str) return template.HTML(str)
}, },
}}, }},
})) }))
m.Use(cache.Cacher()) m.Use(cache.Cacher())
m.Use(captcha.Captchaer()) m.Use(captcha.Captchaer())
m.Use(session.Sessioner(session.Options{ m.Use(session.Sessioner(session.Options{
Provider: "file", Provider: "file",
ProviderConfig: app.DataDir + "/sessions", ProviderConfig: app.DataDir + "/sessions",
})) }))
m.Use(toolbox.Toolboxer(m)) m.Use(toolbox.Toolboxer(m))
checkAppInstall(m) checkAppInstall(m)
m.Use(func(ctx *macaron.Context, sess session.Store){ m.Use(func(ctx *macaron.Context, sess session.Store) {
if app.Installed { if app.Installed {
ipAuth(ctx) ipAuth(ctx)
userAuth(ctx, sess) userAuth(ctx, sess)
setShareData(ctx, sess) setShareData(ctx, sess)
} }
}) })
} }
// region 自定义中间件 // region 自定义中间件
/** 系统未安装,重定向到安装页面 **/ /** 系统未安装,重定向到安装页面 **/
func checkAppInstall(m *macaron.Macaron) { func checkAppInstall(m *macaron.Macaron) {
m.Use(func(ctx *macaron.Context) { m.Use(func(ctx *macaron.Context) {
installUrl := "/install" installUrl := "/install"
if strings.HasPrefix(ctx.Req.URL.Path, installUrl) { if strings.HasPrefix(ctx.Req.URL.Path, installUrl) {
return return
} }
if !app.Installed { if !app.Installed {
ctx.Redirect(installUrl) ctx.Redirect(installUrl)
} }
}) })
} }
// IP验证, 通过反向代理访问gocron需设置Header X-Real-IP才能获取到客户端真实IP // IP验证, 通过反向代理访问gocron需设置Header X-Real-IP才能获取到客户端真实IP
func ipAuth(ctx *macaron.Context) { func ipAuth(ctx *macaron.Context) {
allowIpsStr := app.Setting.AllowIps allowIpsStr := app.Setting.AllowIps
if allowIpsStr == "" { if allowIpsStr == "" {
return return
} }
clientIp := ctx.RemoteAddr() clientIp := ctx.RemoteAddr()
allowIps := strings.Split(allowIpsStr, ",") allowIps := strings.Split(allowIpsStr, ",")
if !utils.InStringSlice(allowIps, clientIp) { if !utils.InStringSlice(allowIps, clientIp) {
logger.Warnf("非法IP访问-%s", clientIp) logger.Warnf("非法IP访问-%s", clientIp)
ctx.Status(403) ctx.Status(403)
} }
} }
// 用户认证 // 用户认证
func userAuth(ctx *macaron.Context, sess session.Store) { func userAuth(ctx *macaron.Context, sess session.Store) {
if user.IsLogin(sess) { if user.IsLogin(sess) {
return return
} }
uri := ctx.Req.URL.Path uri := ctx.Req.URL.Path
found := false found := false
excludePaths := []string{"/install", "/user/login", "/api"} excludePaths := []string{"/install", "/user/login", "/api"}
for _, path := range excludePaths { for _, path := range excludePaths {
if strings.HasPrefix(uri, path) { if strings.HasPrefix(uri, path) {
found = true found = true
break break
} }
} }
if !found { if !found {
ctx.Redirect("/user/login") ctx.Redirect("/user/login")
} }
} }
/** 设置共享数据 **/ /** 设置共享数据 **/
func setShareData(ctx *macaron.Context, sess session.Store) { func setShareData(ctx *macaron.Context, sess session.Store) {
ctx.Data["URI"] = ctx.Req.URL.Path ctx.Data["URI"] = ctx.Req.URL.Path
urlPath := strings.TrimPrefix(ctx.Req.URL.Path, "/") urlPath := strings.TrimPrefix(ctx.Req.URL.Path, "/")
paths := strings.Split(urlPath, "/") paths := strings.Split(urlPath, "/")
ctx.Data["Controller"] = "" ctx.Data["Controller"] = ""
ctx.Data["Action"] = "" ctx.Data["Action"] = ""
if len(paths) > 0 { if len(paths) > 0 {
ctx.Data["Controller"] = paths[0] ctx.Data["Controller"] = paths[0]
} }
if len(paths) > 1 { if len(paths) > 1 {
ctx.Data["Action"] = paths[1] ctx.Data["Action"] = paths[1]
} }
ctx.Data["LoginUsername"] = user.Username(sess) ctx.Data["LoginUsername"] = user.Username(sess)
ctx.Data["LoginUid"] = user.Uid(sess) ctx.Data["LoginUid"] = user.Uid(sess)
ctx.Data["AppName"] = app.Setting.AppName ctx.Data["AppName"] = app.Setting.AppName
} }
/** API接口签名验证 **/ /** API接口签名验证 **/
func apiAuth(ctx *macaron.Context) { func apiAuth(ctx *macaron.Context) {
if !app.Setting.ApiSignEnable { if !app.Setting.ApiSignEnable {
return return
} }
apiKey := strings.TrimSpace(app.Setting.ApiKey) apiKey := strings.TrimSpace(app.Setting.ApiKey)
apiSecret := strings.TrimSpace(app.Setting.ApiSecret) apiSecret := strings.TrimSpace(app.Setting.ApiSecret)
json := utils.JsonResponse{} json := utils.JsonResponse{}
if apiKey == "" || apiSecret == "" { if apiKey == "" || apiSecret == "" {
msg := json.CommonFailure("使用API前, 请先配置密钥") msg := json.CommonFailure("使用API前, 请先配置密钥")
ctx.Write([]byte(msg)) ctx.Write([]byte(msg))
return return
} }
currentTimestamp := time.Now().Unix() currentTimestamp := time.Now().Unix()
time := ctx.QueryInt64("time") time := ctx.QueryInt64("time")
if time <= 0 { if time <= 0 {
msg := json.CommonFailure("参数time不能为空") msg := json.CommonFailure("参数time不能为空")
ctx.Write([]byte(msg)) ctx.Write([]byte(msg))
return return
} }
if time < (currentTimestamp - 1800) { if time < (currentTimestamp - 1800) {
msg := json.CommonFailure("time无效") msg := json.CommonFailure("time无效")
ctx.Write([]byte(msg)) ctx.Write([]byte(msg))
return return
} }
sign := ctx.QueryTrim("sign") sign := ctx.QueryTrim("sign")
if sign == "" { if sign == "" {
msg := json.CommonFailure("参数sign不能为空") msg := json.CommonFailure("参数sign不能为空")
ctx.Write([]byte(msg)) ctx.Write([]byte(msg))
return return
} }
raw := apiKey + strconv.FormatInt(time, 10) + strings.TrimSpace(ctx.Req.URL.Path) + apiSecret raw := apiKey + strconv.FormatInt(time, 10) + strings.TrimSpace(ctx.Req.URL.Path) + apiSecret
realSign := utils.Md5(raw) realSign := utils.Md5(raw)
if sign != realSign { if sign != realSign {
msg := json.CommonFailure("签名验证失败") msg := json.CommonFailure("签名验证失败")
ctx.Write([]byte(msg)) ctx.Write([]byte(msg))
return return
} }
} }
// endregion // endregion
func isAjaxRequest(ctx *macaron.Context) bool { func isAjaxRequest(ctx *macaron.Context) bool {
req := ctx.Req.Header.Get("X-Requested-With") req := ctx.Req.Header.Get("X-Requested-With")
if req == "XMLHttpRequest" { if req == "XMLHttpRequest" {
return true return true
} }
return false return false
} }
func isGetRequest(ctx *macaron.Context) bool { func isGetRequest(ctx *macaron.Context) bool {
return ctx.Req.Method == "GET" return ctx.Req.Method == "GET"
} }

View File

@ -1,335 +1,334 @@
package task package task
import ( import (
"gopkg.in/macaron.v1" "fmt"
"github.com/ouqiang/gocron/models" "github.com/Unknwon/paginater"
"github.com/ouqiang/gocron/modules/logger" "github.com/go-macaron/binding"
"github.com/ouqiang/gocron/modules/utils" "github.com/jakecoffman/cron"
"github.com/ouqiang/gocron/service" "github.com/ouqiang/gocron/models"
"strconv" "github.com/ouqiang/gocron/modules/logger"
"github.com/jakecoffman/cron" "github.com/ouqiang/gocron/modules/utils"
"github.com/Unknwon/paginater" "github.com/ouqiang/gocron/routers/base"
"fmt" "github.com/ouqiang/gocron/service"
"html/template" "gopkg.in/macaron.v1"
"github.com/ouqiang/gocron/routers/base" "html/template"
"github.com/go-macaron/binding" "strconv"
"strings" "strings"
) )
type TaskForm struct { type TaskForm struct {
Id int Id int
Level models.TaskLevel `binding:"Required;In(1,2)"` Level models.TaskLevel `binding:"Required;In(1,2)"`
DependencyStatus models.TaskDependencyStatus DependencyStatus models.TaskDependencyStatus
DependencyTaskId string DependencyTaskId string
Name string `binding:"Required;MaxSize(32)"` Name string `binding:"Required;MaxSize(32)"`
Spec string Spec string
Protocol models.TaskProtocol `binding:"In(1,2)"` Protocol models.TaskProtocol `binding:"In(1,2)"`
Command string `binding:"Required;MaxSize(256)"` Command string `binding:"Required;MaxSize(256)"`
Timeout int `binding:"Range(0,86400)"` Timeout int `binding:"Range(0,86400)"`
Multi int8 `binding:"In(1,2)"` Multi int8 `binding:"In(1,2)"`
RetryTimes int8 RetryTimes int8
HostId string HostId string
Tag string Tag string
Remark string Remark string
NotifyStatus int8 `binding:"In(1,2,3)"` NotifyStatus int8 `binding:"In(1,2,3)"`
NotifyType int8 `binding:"In(1,2,3)"` NotifyType int8 `binding:"In(1,2,3)"`
NotifyReceiverId string NotifyReceiverId string
} }
func (f TaskForm) Error(ctx *macaron.Context, errs binding.Errors) { func (f TaskForm) Error(ctx *macaron.Context, errs binding.Errors) {
if len(errs) == 0 { if len(errs) == 0 {
return return
} }
json := utils.JsonResponse{} json := utils.JsonResponse{}
content := json.CommonFailure("表单验证失败, 请检测输入") content := json.CommonFailure("表单验证失败, 请检测输入")
ctx.Resp.Write([]byte(content)) ctx.Resp.Write([]byte(content))
} }
// 首页 // 首页
func Index(ctx *macaron.Context) { func Index(ctx *macaron.Context) {
taskModel := new(models.Task) taskModel := new(models.Task)
queryParams := parseQueryParams(ctx) queryParams := parseQueryParams(ctx)
total, err := taskModel.Total(queryParams) total, err := taskModel.Total(queryParams)
if err != nil { if err != nil {
logger.Error(err) logger.Error(err)
} }
tasks, err := taskModel.List(queryParams) tasks, err := taskModel.List(queryParams)
if err != nil { if err != nil {
logger.Error(err) logger.Error(err)
} }
name, ok := queryParams["name"].(string) name, ok := queryParams["name"].(string)
var safeNameHTML = "" var safeNameHTML = ""
if ok { if ok {
safeNameHTML = template.HTMLEscapeString(name) safeNameHTML = template.HTMLEscapeString(name)
} }
PageParams := fmt.Sprintf("id=%d&host_id=%d&name=%s&protocol=%d&tag=%s&status=%d&page_size=%d", 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["Id"], queryParams["HostId"], safeNameHTML, queryParams["Protocol"], queryParams["Tag"], queryParams["Status"], queryParams["PageSize"])
queryParams["PageParams"] = template.URL(PageParams) queryParams["PageParams"] = template.URL(PageParams)
p := paginater.New(int(total), queryParams["PageSize"].(int), queryParams["Page"].(int), 5) p := paginater.New(int(total), queryParams["PageSize"].(int), queryParams["Page"].(int), 5)
ctx.Data["Pagination"] = p ctx.Data["Pagination"] = p
setHostsToTemplate(ctx) setHostsToTemplate(ctx)
ctx.Data["Params"] = queryParams ctx.Data["Params"] = queryParams
ctx.Data["Title"] = "任务列表" ctx.Data["Title"] = "任务列表"
ctx.Data["Tasks"] = tasks ctx.Data["Tasks"] = tasks
ctx.HTML(200, "task/index") ctx.HTML(200, "task/index")
} }
// 新增页面 // 新增页面
func Create(ctx *macaron.Context) { func Create(ctx *macaron.Context) {
setHostsToTemplate(ctx) setHostsToTemplate(ctx)
ctx.Data["Title"] = "添加任务" ctx.Data["Title"] = "添加任务"
ctx.HTML(200, "task/task_form") ctx.HTML(200, "task/task_form")
} }
// 编辑页面 // 编辑页面
func Edit(ctx *macaron.Context) { func Edit(ctx *macaron.Context) {
id := ctx.ParamsInt(":id") id := ctx.ParamsInt(":id")
taskModel := new(models.Task) taskModel := new(models.Task)
task, err := taskModel.Detail(id) task, err := taskModel.Detail(id)
if err != nil || task.Id != id { if err != nil || task.Id != id {
logger.Errorf("编辑任务#获取任务详情失败#任务ID-%d#%s", id, err.Error()) logger.Errorf("编辑任务#获取任务详情失败#任务ID-%d#%s", id, err.Error())
ctx.Redirect("/task") ctx.Redirect("/task")
} }
hostModel := new(models.Host) hostModel := new(models.Host)
hostModel.PageSize = -1 hostModel.PageSize = -1
hosts, err := hostModel.List(models.CommonMap{}) hosts, err := hostModel.List(models.CommonMap{})
if err != nil { if err != nil {
logger.Error(err) logger.Error(err)
} else { } else {
for i, host := range(hosts) { for i, host := range hosts {
if inHosts(task.Hosts, host.Id) { if inHosts(task.Hosts, host.Id) {
hosts[i].Selected = true hosts[i].Selected = true
} }
} }
} }
ctx.Data["Task"] = task ctx.Data["Task"] = task
ctx.Data["Hosts"] = hosts ctx.Data["Hosts"] = hosts
ctx.Data["Title"] = "编辑" ctx.Data["Title"] = "编辑"
ctx.HTML(200, "task/task_form") ctx.HTML(200, "task/task_form")
} }
// 保存任务 // 保存任务
func Store(ctx *macaron.Context, form TaskForm) string { func Store(ctx *macaron.Context, form TaskForm) string {
json := utils.JsonResponse{} json := utils.JsonResponse{}
taskModel := models.Task{} taskModel := models.Task{}
var id int = form.Id var id int = form.Id
nameExists, err := taskModel.NameExist(form.Name, form.Id) nameExists, err := taskModel.NameExist(form.Name, form.Id)
if err != nil { if err != nil {
return json.CommonFailure(utils.FailureContent, err) return json.CommonFailure(utils.FailureContent, err)
} }
if nameExists { if nameExists {
return json.CommonFailure("任务名称已存在") return json.CommonFailure("任务名称已存在")
} }
if form.Protocol == models.TaskRPC && form.HostId == "" { if form.Protocol == models.TaskRPC && form.HostId == "" {
return json.CommonFailure("请选择主机名") return json.CommonFailure("请选择主机名")
} }
taskModel.Name = form.Name taskModel.Name = form.Name
taskModel.Protocol = form.Protocol taskModel.Protocol = form.Protocol
taskModel.Command = form.Command taskModel.Command = form.Command
taskModel.Timeout = form.Timeout taskModel.Timeout = form.Timeout
taskModel.Tag = form.Tag taskModel.Tag = form.Tag
taskModel.Remark = form.Remark taskModel.Remark = form.Remark
taskModel.Multi = form.Multi taskModel.Multi = form.Multi
taskModel.RetryTimes = form.RetryTimes taskModel.RetryTimes = form.RetryTimes
if taskModel.Multi != 1 { if taskModel.Multi != 1 {
taskModel.Multi = 0 taskModel.Multi = 0
} }
taskModel.NotifyStatus = form.NotifyStatus - 1 taskModel.NotifyStatus = form.NotifyStatus - 1
taskModel.NotifyType = form.NotifyType - 1 taskModel.NotifyType = form.NotifyType - 1
taskModel.NotifyReceiverId = form.NotifyReceiverId taskModel.NotifyReceiverId = form.NotifyReceiverId
taskModel.Spec = form.Spec taskModel.Spec = form.Spec
taskModel.Level = form.Level taskModel.Level = form.Level
taskModel.DependencyStatus = form.DependencyStatus taskModel.DependencyStatus = form.DependencyStatus
taskModel.DependencyTaskId = strings.TrimSpace(form.DependencyTaskId) taskModel.DependencyTaskId = strings.TrimSpace(form.DependencyTaskId)
if taskModel.NotifyStatus > 0 && taskModel.NotifyReceiverId == "" { if taskModel.NotifyStatus > 0 && taskModel.NotifyReceiverId == "" {
return json.CommonFailure("至少选择一个通知接收者") return json.CommonFailure("至少选择一个通知接收者")
} }
if taskModel.Protocol == models.TaskHTTP { if taskModel.Protocol == models.TaskHTTP {
command := strings.ToLower(taskModel.Command) command := strings.ToLower(taskModel.Command)
if !strings.HasPrefix(command, "http://") && !strings.HasPrefix(command, "https://") { if !strings.HasPrefix(command, "http://") && !strings.HasPrefix(command, "https://") {
return json.CommonFailure("请输入正确的URL地址") return json.CommonFailure("请输入正确的URL地址")
} }
if taskModel.Timeout > 300 { if taskModel.Timeout > 300 {
return json.CommonFailure("HTTP任务超时时间不能超过300秒") return json.CommonFailure("HTTP任务超时时间不能超过300秒")
} }
} }
if taskModel.RetryTimes > 10 || taskModel.RetryTimes < 0 { if taskModel.RetryTimes > 10 || taskModel.RetryTimes < 0 {
return json.CommonFailure("任务重试次数取值0-10") return json.CommonFailure("任务重试次数取值0-10")
} }
if (taskModel.DependencyStatus != models.TaskDependencyStatusStrong && if taskModel.DependencyStatus != models.TaskDependencyStatusStrong &&
taskModel.DependencyStatus != models.TaskDependencyStatusWeak) { taskModel.DependencyStatus != models.TaskDependencyStatusWeak {
return json.CommonFailure("请选择依赖关系") return json.CommonFailure("请选择依赖关系")
} }
if taskModel.Level == models.TaskLevelParent { if taskModel.Level == models.TaskLevelParent {
_, err = cron.Parse(form.Spec) _, err = cron.Parse(form.Spec)
if err != nil { if err != nil {
return json.CommonFailure("crontab表达式解析失败", err) return json.CommonFailure("crontab表达式解析失败", err)
} }
} else { } else {
taskModel.DependencyTaskId = "" taskModel.DependencyTaskId = ""
taskModel.Spec = "" taskModel.Spec = ""
} }
if id > 0 && taskModel.DependencyTaskId != "" { if id > 0 && taskModel.DependencyTaskId != "" {
dependencyTaskIds := strings.Split(taskModel.DependencyTaskId, ",") dependencyTaskIds := strings.Split(taskModel.DependencyTaskId, ",")
if utils.InStringSlice(dependencyTaskIds, strconv.Itoa(id)) { if utils.InStringSlice(dependencyTaskIds, strconv.Itoa(id)) {
return json.CommonFailure("不允许设置当前任务为子任务") return json.CommonFailure("不允许设置当前任务为子任务")
} }
} }
if id == 0 { if id == 0 {
// 任务添加后开始调度执行 // 任务添加后开始调度执行
taskModel.Status = models.Running taskModel.Status = models.Running
id, err = taskModel.Create() id, err = taskModel.Create()
} else { } else {
_, err = taskModel.UpdateBean(id) _, err = taskModel.UpdateBean(id)
} }
if err != nil { if err != nil {
return json.CommonFailure("保存失败", err) return json.CommonFailure("保存失败", err)
} }
taskHostModel := new(models.TaskHost) taskHostModel := new(models.TaskHost)
if form.Protocol == models.TaskRPC { if form.Protocol == models.TaskRPC {
hostIdStrList := strings.Split(form.HostId, ",") hostIdStrList := strings.Split(form.HostId, ",")
hostIds := make([]int, len(hostIdStrList)) hostIds := make([]int, len(hostIdStrList))
for i, hostIdStr := range hostIdStrList { for i, hostIdStr := range hostIdStrList {
hostIds[i], _ = strconv.Atoi(hostIdStr) hostIds[i], _ = strconv.Atoi(hostIdStr)
} }
taskHostModel.Add(id, hostIds) taskHostModel.Add(id, hostIds)
} else { } else {
taskHostModel.Remove(id) taskHostModel.Remove(id)
} }
status, err := taskModel.GetStatus(id) status, err := taskModel.GetStatus(id)
if status == models.Enabled && taskModel.Level == models.TaskLevelParent { if status == models.Enabled && taskModel.Level == models.TaskLevelParent {
addTaskToTimer(id) addTaskToTimer(id)
} }
return json.Success("保存成功", nil) return json.Success("保存成功", nil)
} }
// 删除任务 // 删除任务
func Remove(ctx *macaron.Context) string { func Remove(ctx *macaron.Context) string {
id := ctx.ParamsInt(":id") id := ctx.ParamsInt(":id")
json := utils.JsonResponse{} json := utils.JsonResponse{}
taskModel := new(models.Task) taskModel := new(models.Task)
_, err := taskModel.Delete(id) _, err := taskModel.Delete(id)
if err != nil { if err != nil {
return json.CommonFailure(utils.FailureContent, err) return json.CommonFailure(utils.FailureContent, err)
} }
taskHostModel := new(models.TaskHost) taskHostModel := new(models.TaskHost)
taskHostModel.Remove(id) 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 { func Enable(ctx *macaron.Context) string {
return changeStatus(ctx, models.Enabled) return changeStatus(ctx, models.Enabled)
} }
// 暂停任务 // 暂停任务
func Disable(ctx *macaron.Context) string { func Disable(ctx *macaron.Context) string {
return changeStatus(ctx, models.Disabled) return changeStatus(ctx, models.Disabled)
} }
// 手动运行任务 // 手动运行任务
func Run(ctx *macaron.Context) string { func Run(ctx *macaron.Context) string {
id := ctx.ParamsInt(":id") id := ctx.ParamsInt(":id")
json := utils.JsonResponse{} json := utils.JsonResponse{}
taskModel := new(models.Task) taskModel := new(models.Task)
task , err := taskModel.Detail(id) task, err := taskModel.Detail(id)
if err != nil || task.Id <= 0 { if err != nil || task.Id <= 0 {
return json.CommonFailure("获取任务详情失败", err) return json.CommonFailure("获取任务详情失败", err)
} }
task.Spec = "手动运行" task.Spec = "手动运行"
serviceTask := new(service.Task) serviceTask := new(service.Task)
serviceTask.Run(task) serviceTask.Run(task)
return json.Success("任务已开始运行, 请到任务日志中查看结果", nil); return json.Success("任务已开始运行, 请到任务日志中查看结果", nil)
} }
// 改变任务状态 // 改变任务状态
func changeStatus(ctx *macaron.Context, status models.Status) string { func changeStatus(ctx *macaron.Context, status models.Status) string {
id := ctx.ParamsInt(":id") id := ctx.ParamsInt(":id")
json := utils.JsonResponse{} json := utils.JsonResponse{}
taskModel := new(models.Task) taskModel := new(models.Task)
_, err := taskModel.Update(id, models.CommonMap{ _, err := taskModel.Update(id, models.CommonMap{
"Status": status, "Status": status,
}) })
if err != nil { if err != nil {
return json.CommonFailure(utils.FailureContent, err) return json.CommonFailure(utils.FailureContent, err)
} }
if status == models.Enabled { if status == models.Enabled {
addTaskToTimer(id) addTaskToTimer(id)
} else { } else {
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 addTaskToTimer(id int) { func addTaskToTimer(id int) {
taskModel := new(models.Task) taskModel := new(models.Task)
task, err := taskModel.Detail(id) task, err := taskModel.Detail(id)
if err != nil { if err != nil {
logger.Error(err) logger.Error(err)
return return
} }
taskService := service.Task{} taskService := service.Task{}
taskService.Add(task) taskService.Add(task)
} }
// 解析查询参数 // 解析查询参数
func parseQueryParams(ctx *macaron.Context) (models.CommonMap) { func parseQueryParams(ctx *macaron.Context) models.CommonMap {
var params models.CommonMap = models.CommonMap{} var params models.CommonMap = models.CommonMap{}
params["Id"] = ctx.QueryInt("id") params["Id"] = ctx.QueryInt("id")
params["HostId"] = ctx.QueryInt("host_id") params["HostId"] = ctx.QueryInt("host_id")
params["Name"] = ctx.QueryTrim("name") params["Name"] = ctx.QueryTrim("name")
params["Protocol"] = ctx.QueryInt("protocol") params["Protocol"] = ctx.QueryInt("protocol")
params["Tag"] = ctx.QueryTrim("tag") params["Tag"] = ctx.QueryTrim("tag")
status := ctx.QueryInt("status") status := ctx.QueryInt("status")
if status >=0 { if status >= 0 {
status -= 1 status -= 1
} }
params["Status"] = status params["Status"] = status
base.ParsePageAndPageSize(ctx, params) base.ParsePageAndPageSize(ctx, params)
return params return params
} }
func setHostsToTemplate(ctx *macaron.Context) { func setHostsToTemplate(ctx *macaron.Context) {
hostModel := new(models.Host) hostModel := new(models.Host)
hostModel.PageSize = -1 hostModel.PageSize = -1
hosts, err := hostModel.List(models.CommonMap{}) hosts, err := hostModel.List(models.CommonMap{})
if err != nil { if err != nil {
logger.Error(err) logger.Error(err)
} }
ctx.Data["Hosts"] = hosts ctx.Data["Hosts"] = hosts
} }
func inHosts(slice []models.TaskHostDetail, element int16) bool { func inHosts(slice []models.TaskHostDetail, element int16) bool {
for _, v := range slice { for _, v := range slice {
if v.HostId == element { if v.HostId == element {
return true return true
} }
} }
return false return false
} }

View File

@ -3,78 +3,78 @@ package tasklog
// 任务日志 // 任务日志
import ( import (
"gopkg.in/macaron.v1" "fmt"
"github.com/ouqiang/gocron/models" "github.com/Unknwon/paginater"
"github.com/ouqiang/gocron/modules/logger" "github.com/ouqiang/gocron/models"
"github.com/ouqiang/gocron/modules/utils" "github.com/ouqiang/gocron/modules/logger"
"github.com/Unknwon/paginater" "github.com/ouqiang/gocron/modules/utils"
"fmt" "github.com/ouqiang/gocron/routers/base"
"html/template" "gopkg.in/macaron.v1"
"github.com/ouqiang/gocron/routers/base" "html/template"
) )
func Index(ctx *macaron.Context) { func Index(ctx *macaron.Context) {
logModel := new(models.TaskLog) logModel := new(models.TaskLog)
queryParams := parseQueryParams(ctx) queryParams := parseQueryParams(ctx)
total, err := logModel.Total(queryParams) total, err := logModel.Total(queryParams)
if err != nil { if err != nil {
logger.Error(err) logger.Error(err)
} }
logs, err := logModel.List(queryParams) logs, err := logModel.List(queryParams)
if err != nil { if err != nil {
logger.Error(err) logger.Error(err)
} }
PageParams := fmt.Sprintf("task_id=%d&protocol=%d&status=%d&page_size=%d", PageParams := fmt.Sprintf("task_id=%d&protocol=%d&status=%d&page_size=%d",
queryParams["TaskId"], queryParams["Protocol"], queryParams["Status"], queryParams["TaskId"], queryParams["Protocol"], queryParams["Status"],
queryParams["PageSize"]); queryParams["PageSize"])
queryParams["PageParams"] = template.URL(PageParams) queryParams["PageParams"] = template.URL(PageParams)
p := paginater.New(int(total), queryParams["PageSize"].(int), queryParams["Page"].(int), 5) p := paginater.New(int(total), queryParams["PageSize"].(int), queryParams["Page"].(int), 5)
ctx.Data["Pagination"] = p ctx.Data["Pagination"] = p
ctx.Data["Title"] = "任务日志" ctx.Data["Title"] = "任务日志"
ctx.Data["Logs"] = logs ctx.Data["Logs"] = logs
ctx.Data["Params"] = queryParams ctx.Data["Params"] = queryParams
ctx.HTML(200, "task/log") ctx.HTML(200, "task/log")
} }
// 清空日志 // 清空日志
func Clear(ctx *macaron.Context) string { func Clear(ctx *macaron.Context) string {
taskLogModel := new(models.TaskLog) taskLogModel := new(models.TaskLog)
_, err := taskLogModel.Clear() _, err := taskLogModel.Clear()
json := utils.JsonResponse{} json := utils.JsonResponse{}
if err != nil { if err != nil {
return json.CommonFailure(utils.FailureContent) return json.CommonFailure(utils.FailureContent)
} }
return json.Success(utils.SuccessContent, nil) return json.Success(utils.SuccessContent, nil)
} }
// 删除N个月前的日志 // 删除N个月前的日志
func Remove(ctx *macaron.Context) string { func Remove(ctx *macaron.Context) string {
month := ctx.ParamsInt(":id") month := ctx.ParamsInt(":id")
json := utils.JsonResponse{} json := utils.JsonResponse{}
if month < 1 || month > 12 { if month < 1 || month > 12 {
return json.CommonFailure("参数取值范围1-12") return json.CommonFailure("参数取值范围1-12")
} }
taskLogModel := new(models.TaskLog) taskLogModel := new(models.TaskLog)
_, err := taskLogModel.Remove(month) _, err := taskLogModel.Remove(month)
if err != nil { if err != nil {
return json.CommonFailure("删除失败", err) return json.CommonFailure("删除失败", err)
} }
return json.Success("删除成功", nil) return json.Success("删除成功", nil)
} }
// 解析查询参数 // 解析查询参数
func parseQueryParams(ctx *macaron.Context) (models.CommonMap) { func parseQueryParams(ctx *macaron.Context) models.CommonMap {
var params models.CommonMap = models.CommonMap{} var params models.CommonMap = models.CommonMap{}
params["TaskId"] = ctx.QueryInt("task_id") params["TaskId"] = ctx.QueryInt("task_id")
params["Protocol"] = ctx.QueryInt("protocol") params["Protocol"] = ctx.QueryInt("protocol")
status := ctx.QueryInt("status") status := ctx.QueryInt("status")
if status >=0 { if status >= 0 {
status -= 1 status -= 1
} }
params["Status"] = status params["Status"] = status
base.ParsePageAndPageSize(ctx, params) base.ParsePageAndPageSize(ctx, params)
return params return params
} }

View File

@ -1,127 +1,126 @@
package user package user
import ( import (
"gopkg.in/macaron.v1" "github.com/go-macaron/captcha"
"github.com/ouqiang/gocron/modules/utils" "github.com/go-macaron/session"
"github.com/ouqiang/gocron/models" "github.com/ouqiang/gocron/models"
"github.com/go-macaron/session" "github.com/ouqiang/gocron/modules/logger"
"github.com/ouqiang/gocron/modules/logger" "github.com/ouqiang/gocron/modules/utils"
"github.com/go-macaron/captcha" "gopkg.in/macaron.v1"
) )
// @author qiang.ou<qingqianludao@gmail.com> // @author qiang.ou<qingqianludao@gmail.com>
// @date 2017/4/23-14:16 // @date 2017/4/23-14:16
func Login(ctx *macaron.Context) { func Login(ctx *macaron.Context) {
ctx.Data["Title"] = "用户登录" ctx.Data["Title"] = "用户登录"
ctx.HTML(200, "user/login") ctx.HTML(200, "user/login")
} }
func EditPassword(ctx *macaron.Context) { func EditPassword(ctx *macaron.Context) {
ctx.Data["Title"] = "修改密码" ctx.Data["Title"] = "修改密码"
ctx.HTML(200, "user/editPassword") ctx.HTML(200, "user/editPassword")
} }
func UpdatePassword(ctx *macaron.Context, sess session.Store) string { func UpdatePassword(ctx *macaron.Context, sess session.Store) string {
oldPassword := ctx.QueryTrim("old_password") oldPassword := ctx.QueryTrim("old_password")
newPassword := ctx.QueryTrim("new_password") newPassword := ctx.QueryTrim("new_password")
confirmNewPassword := ctx.QueryTrim("confirm_new_password") confirmNewPassword := ctx.QueryTrim("confirm_new_password")
json := utils.JsonResponse{} json := utils.JsonResponse{}
if oldPassword == "" || newPassword == "" || confirmNewPassword == "" { if oldPassword == "" || newPassword == "" || confirmNewPassword == "" {
return json.CommonFailure("原密码和新密码均不能为空") return json.CommonFailure("原密码和新密码均不能为空")
} }
if newPassword != confirmNewPassword { if newPassword != confirmNewPassword {
return json.CommonFailure("两次输入密码不一致") return json.CommonFailure("两次输入密码不一致")
} }
if oldPassword == newPassword { if oldPassword == newPassword {
return json.CommonFailure("原密码与新密码不能相同") return json.CommonFailure("原密码与新密码不能相同")
} }
userModel := new(models.User) userModel := new(models.User)
if !userModel.Match(Username(sess), oldPassword) { if !userModel.Match(Username(sess), oldPassword) {
return json.CommonFailure("原密码输入错误") return json.CommonFailure("原密码输入错误")
} }
_, err := userModel.UpdatePassword(Uid(sess), newPassword) _, err := userModel.UpdatePassword(Uid(sess), newPassword)
if err != nil { if err != nil {
return json.CommonFailure("修改失败") return json.CommonFailure("修改失败")
} }
return json.Success("修改成功", nil) return json.Success("修改成功", nil)
} }
func ValidateLogin(ctx *macaron.Context, sess session.Store, cpt *captcha.Captcha) string { func ValidateLogin(ctx *macaron.Context, sess session.Store, cpt *captcha.Captcha) string {
username := ctx.QueryTrim("username") username := ctx.QueryTrim("username")
password := ctx.QueryTrim("password") password := ctx.QueryTrim("password")
json := utils.JsonResponse{} json := utils.JsonResponse{}
if username == "" || password == "" { if username == "" || password == "" {
return json.CommonFailure("用户名、密码不能为空") return json.CommonFailure("用户名、密码不能为空")
} }
userModel := new (models.User) userModel := new(models.User)
if !userModel.Match(username, password) { if !userModel.Match(username, password) {
return json.CommonFailure("用户名或密码错误") return json.CommonFailure("用户名或密码错误")
} }
if !cpt.VerifyReq(ctx.Req) { if !cpt.VerifyReq(ctx.Req) {
return json.Failure(utils.CaptchaError, "验证码错误") return json.Failure(utils.CaptchaError, "验证码错误")
} }
loginLogModel := new(models.LoginLog) loginLogModel := new(models.LoginLog)
loginLogModel.Username = userModel.Name loginLogModel.Username = userModel.Name
loginLogModel.Ip = ctx.RemoteAddr() loginLogModel.Ip = ctx.RemoteAddr()
_, err := loginLogModel.Create() _, err := loginLogModel.Create()
if err != nil { if err != nil {
logger.Error("记录用户登录日志失败", err) logger.Error("记录用户登录日志失败", err)
} }
sess.Set("username", userModel.Name)
sess.Set("uid", userModel.Id)
sess.Set("isAdmin", userModel.IsAdmin)
sess.Set("username", userModel.Name) return json.Success("登录成功", nil)
sess.Set("uid", userModel.Id)
sess.Set("isAdmin", userModel.IsAdmin)
return json.Success("登录成功", nil)
} }
func Logout(ctx *macaron.Context, sess session.Store) { func Logout(ctx *macaron.Context, sess session.Store) {
if IsLogin(sess) { if IsLogin(sess) {
err := sess.Destory(ctx) err := sess.Destory(ctx)
if err != nil { if err != nil {
logger.Error("用户退出登录失败", err) logger.Error("用户退出登录失败", err)
} }
} }
Login(ctx) Login(ctx)
} }
func Username(sess session.Store) string { func Username(sess session.Store) string {
username,ok := sess.Get("username").(string) username, ok := sess.Get("username").(string)
if ok { if ok {
return username return username
} }
return "" return ""
} }
func Uid(sess session.Store) int { func Uid(sess session.Store) int {
uid,ok := sess.Get("uid").(int) uid, ok := sess.Get("uid").(int)
if ok { if ok {
return uid return uid
} }
return 0 return 0
} }
func IsLogin(sess session.Store) bool { func IsLogin(sess session.Store) bool {
uid, ok := sess.Get("uid").(int) uid, ok := sess.Get("uid").(int)
if ok && uid > 0 { if ok && uid > 0 {
return true return true
} }
return false return false
} }
func IsAdmin(sess session.Store) bool { func IsAdmin(sess session.Store) bool {
isAdmin, ok := sess.Get("isAdmin").(int8) isAdmin, ok := sess.Get("isAdmin").(int8)
if ok && isAdmin > 0 { if ok && isAdmin > 0 {
return true return true
} }
return false return false
} }

View File

@ -1,154 +1,155 @@
package service package service
import ( import (
"github.com/ouqiang/gocron/models" "errors"
"strconv" "fmt"
"time" "github.com/jakecoffman/cron"
"github.com/ouqiang/gocron/modules/logger" "github.com/ouqiang/gocron/models"
"github.com/jakecoffman/cron" "github.com/ouqiang/gocron/modules/httpclient"
"errors" "github.com/ouqiang/gocron/modules/logger"
"fmt" "github.com/ouqiang/gocron/modules/notify"
"github.com/ouqiang/gocron/modules/httpclient" rpcClient "github.com/ouqiang/gocron/modules/rpc/client"
"github.com/ouqiang/gocron/modules/notify" pb "github.com/ouqiang/gocron/modules/rpc/proto"
"sync" "strconv"
rpcClient "github.com/ouqiang/gocron/modules/rpc/client" "strings"
pb "github.com/ouqiang/gocron/modules/rpc/proto" "sync"
"strings" "time"
) )
// 定时任务调度管理器 // 定时任务调度管理器
var Cron *cron.Cron var Cron *cron.Cron
// 同一任务是否有实例处于运行中 // 同一任务是否有实例处于运行中
var runInstance Instance var runInstance Instance
// 任务计数-正在运行中的任务 // 任务计数-正在运行中的任务
var TaskNum TaskCount var TaskNum TaskCount
// 任务计数 // 任务计数
type TaskCount struct { type TaskCount struct {
num int num int
sync.RWMutex sync.RWMutex
} }
func (c *TaskCount) Add() { func (c *TaskCount) Add() {
c.Lock() c.Lock()
defer c.Unlock() defer c.Unlock()
c.num += 1 c.num += 1
} }
func (c *TaskCount) Done() { func (c *TaskCount) Done() {
c.Lock() c.Lock()
defer c.Unlock() defer c.Unlock()
c.num -= 1 c.num -= 1
} }
func (c *TaskCount) Num() int { func (c *TaskCount) Num() int {
c.RLock() c.RLock()
defer c.RUnlock() defer c.RUnlock()
return c.num return c.num
} }
// 任务ID作为Key // 任务ID作为Key
type Instance struct { type Instance struct {
Status map[int]bool Status map[int]bool
sync.RWMutex sync.RWMutex
} }
// 是否有任务处于运行中 // 是否有任务处于运行中
func (i *Instance) has(key int) bool { func (i *Instance) has(key int) bool {
i.RLock() i.RLock()
defer i.RUnlock() defer i.RUnlock()
running, ok := i.Status[key] running, ok := i.Status[key]
if ok && running { if ok && running {
return true return true
} }
return false return false
} }
func (i *Instance) add(key int) { func (i *Instance) add(key int) {
i.Lock() i.Lock()
defer i.Unlock() defer i.Unlock()
i.Status[key] = true i.Status[key] = true
} }
func (i *Instance) done(key int) { func (i *Instance) done(key int) {
i.Lock() i.Lock()
defer i.Unlock() defer i.Unlock()
delete(i.Status, key) delete(i.Status, key)
} }
type Task struct{} type Task struct{}
type TaskResult struct { type TaskResult struct {
Result string Result string
Err error Err error
RetryTimes int8 RetryTimes int8
} }
// 初始化任务, 从数据库取出所有任务, 添加到定时任务并运行 // 初始化任务, 从数据库取出所有任务, 添加到定时任务并运行
func (task *Task) Initialize() { func (task *Task) Initialize() {
Cron = cron.New() Cron = cron.New()
Cron.Start() Cron.Start()
runInstance = Instance{make(map[int]bool), sync.RWMutex{}} runInstance = Instance{make(map[int]bool), sync.RWMutex{}}
TaskNum = TaskCount{0, sync.RWMutex{}} TaskNum = TaskCount{0, sync.RWMutex{}}
taskModel := new(models.Task) taskModel := new(models.Task)
taskList, err := taskModel.ActiveList() taskList, err := taskModel.ActiveList()
if err != nil { if err != nil {
logger.Error("定时任务初始化#获取任务列表错误-", err.Error()) logger.Error("定时任务初始化#获取任务列表错误-", err.Error())
return return
} }
if len(taskList) == 0 { if len(taskList) == 0 {
logger.Debug("任务列表为空") logger.Debug("任务列表为空")
return return
} }
task.BatchAdd(taskList) task.BatchAdd(taskList)
} }
// 批量添加任务 // 批量添加任务
func (task *Task) BatchAdd(tasks []models.Task) { func (task *Task) BatchAdd(tasks []models.Task) {
for _, item := range tasks { for _, item := range tasks {
task.Add(item) task.Add(item)
} }
} }
// 添加任务 // 添加任务
func (task *Task) Add(taskModel models.Task) { func (task *Task) Add(taskModel models.Task) {
if taskModel.Level == models.TaskLevelChild { if taskModel.Level == models.TaskLevelChild {
logger.Errorf("添加任务失败#不允许添加子任务到调度器#任务Id-%d", taskModel.Id); logger.Errorf("添加任务失败#不允许添加子任务到调度器#任务Id-%d", taskModel.Id)
return return
} }
taskFunc := createJob(taskModel) taskFunc := createJob(taskModel)
if taskFunc == nil { if taskFunc == nil {
logger.Error("创建任务处理Job失败,不支持的任务协议#", taskModel.Protocol) logger.Error("创建任务处理Job失败,不支持的任务协议#", taskModel.Protocol)
return return
} }
cronName := strconv.Itoa(taskModel.Id) cronName := strconv.Itoa(taskModel.Id)
// Cron任务采用数组存储, 删除任务需遍历数组, 并对数组重新赋值, 任务较多时,有性能问题 // Cron任务采用数组存储, 删除任务需遍历数组, 并对数组重新赋值, 任务较多时,有性能问题
Cron.RemoveJob(cronName) Cron.RemoveJob(cronName)
err := Cron.AddFunc(taskModel.Spec, taskFunc, cronName) err := Cron.AddFunc(taskModel.Spec, taskFunc, cronName)
if err != nil { if err != nil {
logger.Error("添加任务到调度器失败#", err) logger.Error("添加任务到调度器失败#", err)
} }
} }
// 停止所有任务 // 停止所有任务
func (task *Task) StopAll() { func (task *Task) StopAll() {
Cron.Stop() Cron.Stop()
} }
// 直接运行任务 // 直接运行任务
func (task *Task) Run(taskModel models.Task) { func (task *Task) Run(taskModel models.Task) {
go createJob(taskModel)() go createJob(taskModel)()
} }
type Handler interface { type Handler interface {
Run(taskModel models.Task) (string, error) Run(taskModel models.Task) (string, error)
} }
// HTTP任务 // HTTP任务
type HTTPHandler struct{} type HTTPHandler struct{}
@ -156,259 +157,257 @@ type HTTPHandler struct{}
const HttpExecTimeout = 300 const HttpExecTimeout = 300
func (h *HTTPHandler) Run(taskModel models.Task) (result string, err error) { func (h *HTTPHandler) Run(taskModel models.Task) (result string, err error) {
if taskModel.Timeout <= 0 || taskModel.Timeout > HttpExecTimeout { if taskModel.Timeout <= 0 || taskModel.Timeout > HttpExecTimeout {
taskModel.Timeout = HttpExecTimeout taskModel.Timeout = HttpExecTimeout
} }
resp := httpclient.Get(taskModel.Command, taskModel.Timeout) resp := httpclient.Get(taskModel.Command, taskModel.Timeout)
// 返回状态码非200均为失败 // 返回状态码非200均为失败
if resp.StatusCode != 200 { if resp.StatusCode != 200 {
return resp.Body, errors.New(fmt.Sprintf("HTTP状态码非200-->%d", resp.StatusCode)) return resp.Body, errors.New(fmt.Sprintf("HTTP状态码非200-->%d", resp.StatusCode))
} }
return resp.Body, err return resp.Body, err
} }
// RPC调用执行任务 // RPC调用执行任务
type RPCHandler struct {} type RPCHandler struct{}
func (h *RPCHandler) Run(taskModel models.Task) (result string, err error) { func (h *RPCHandler) Run(taskModel models.Task) (result string, err error) {
taskRequest := new(pb.TaskRequest) taskRequest := new(pb.TaskRequest)
taskRequest.Timeout = int32(taskModel.Timeout) taskRequest.Timeout = int32(taskModel.Timeout)
taskRequest.Command = taskModel.Command taskRequest.Command = taskModel.Command
var resultChan chan TaskResult = make(chan TaskResult, len(taskModel.Hosts)) var resultChan chan TaskResult = make(chan TaskResult, len(taskModel.Hosts))
for _, taskHost := range taskModel.Hosts { for _, taskHost := range taskModel.Hosts {
go func(th models.TaskHostDetail) { go func(th models.TaskHostDetail) {
output, err := rpcClient.ExecWithRetry(th.Name, th.Port, taskRequest) output, err := rpcClient.Exec(th.Name, th.Port, taskRequest)
var errorMessage string = "" var errorMessage string = ""
if err != nil { if err != nil {
errorMessage = err.Error() errorMessage = err.Error()
} }
outputMessage := fmt.Sprintf("主机: [%s-%s]\n%s\n%s\n\n", outputMessage := fmt.Sprintf("主机: [%s-%s]\n%s\n%s\n\n",
th.Alias, th.Name, errorMessage, output, th.Alias, th.Name, errorMessage, output,
) )
resultChan <- TaskResult{Err:err, Result: outputMessage} resultChan <- TaskResult{Err: err, Result: outputMessage}
}(taskHost) }(taskHost)
} }
var aggregationErr error = nil var aggregationErr error = nil
var aggregationResult string = "" var aggregationResult string = ""
for i := 0; i < len(taskModel.Hosts); i++ { for i := 0; i < len(taskModel.Hosts); i++ {
taskResult := <- resultChan taskResult := <-resultChan
aggregationResult += taskResult.Result aggregationResult += taskResult.Result
if taskResult.Err != nil { if taskResult.Err != nil {
aggregationErr = taskResult.Err aggregationErr = taskResult.Err
} }
} }
return aggregationResult, aggregationErr return aggregationResult, aggregationErr
} }
// 创建任务日志 // 创建任务日志
func createTaskLog(taskModel models.Task, status models.Status) (int64, error) { func createTaskLog(taskModel models.Task, status models.Status) (int64, error) {
taskLogModel := new(models.TaskLog) taskLogModel := new(models.TaskLog)
taskLogModel.TaskId = taskModel.Id taskLogModel.TaskId = taskModel.Id
taskLogModel.Name = taskModel.Name taskLogModel.Name = taskModel.Name
taskLogModel.Spec = taskModel.Spec taskLogModel.Spec = taskModel.Spec
taskLogModel.Protocol = taskModel.Protocol taskLogModel.Protocol = taskModel.Protocol
taskLogModel.Command = taskModel.Command taskLogModel.Command = taskModel.Command
taskLogModel.Timeout = taskModel.Timeout taskLogModel.Timeout = taskModel.Timeout
if taskModel.Protocol == models.TaskRPC { if taskModel.Protocol == models.TaskRPC {
var aggregationHost string = "" var aggregationHost string = ""
for _, host := range taskModel.Hosts { for _, host := range taskModel.Hosts {
aggregationHost += fmt.Sprintf("%s-%s<br>", host.Alias, host.Name) aggregationHost += fmt.Sprintf("%s-%s<br>", host.Alias, host.Name)
} }
taskLogModel.Hostname = aggregationHost taskLogModel.Hostname = aggregationHost
} }
taskLogModel.StartTime = time.Now() taskLogModel.StartTime = time.Now()
taskLogModel.Status = status taskLogModel.Status = status
insertId, err := taskLogModel.Create() insertId, err := taskLogModel.Create()
return insertId, err return insertId, err
} }
// 更新任务日志 // 更新任务日志
func updateTaskLog(taskLogId int64, taskResult TaskResult) (int64, error) { func updateTaskLog(taskLogId int64, taskResult TaskResult) (int64, error) {
taskLogModel := new(models.TaskLog) taskLogModel := new(models.TaskLog)
var status models.Status var status models.Status
var result string = taskResult.Result var result string = taskResult.Result
if taskResult.Err != nil { if taskResult.Err != nil {
status = models.Failure status = models.Failure
} else { } else {
status = models.Finish status = models.Finish
} }
return taskLogModel.Update(taskLogId, models.CommonMap{ return taskLogModel.Update(taskLogId, models.CommonMap{
"retry_times": taskResult.RetryTimes, "retry_times": taskResult.RetryTimes,
"status": status, "status": status,
"result": result, "result": result,
}) })
} }
func createJob(taskModel models.Task) cron.FuncJob { func createJob(taskModel models.Task) cron.FuncJob {
var handler Handler = createHandler(taskModel) var handler Handler = createHandler(taskModel)
if handler == nil { if handler == nil {
return nil return nil
} }
taskFunc := func() { taskFunc := func() {
TaskNum.Add() TaskNum.Add()
defer TaskNum.Done() defer TaskNum.Done()
taskLogId := beforeExecJob(taskModel) taskLogId := beforeExecJob(taskModel)
if taskLogId <= 0 { if taskLogId <= 0 {
return return
} }
logger.Infof("开始执行任务#%s#命令-%s", taskModel.Name, taskModel.Command) logger.Infof("开始执行任务#%s#命令-%s", taskModel.Name, taskModel.Command)
taskResult := execJob(handler, taskModel) taskResult := execJob(handler, taskModel)
logger.Infof("任务完成#%s#命令-%s", taskModel.Name, taskModel.Command) logger.Infof("任务完成#%s#命令-%s", taskModel.Name, taskModel.Command)
afterExecJob(taskModel, taskResult, taskLogId) afterExecJob(taskModel, taskResult, taskLogId)
} }
return taskFunc return taskFunc
} }
func createHandler(taskModel models.Task) Handler { func createHandler(taskModel models.Task) Handler {
var handler Handler = nil var handler Handler = nil
switch taskModel.Protocol { switch taskModel.Protocol {
case models.TaskHTTP: case models.TaskHTTP:
handler = new(HTTPHandler) handler = new(HTTPHandler)
case models.TaskRPC: case models.TaskRPC:
handler = new(RPCHandler) handler = new(RPCHandler)
} }
return handler
return handler;
} }
// 任务前置操作 // 任务前置操作
func beforeExecJob(taskModel models.Task) (taskLogId int64) { func beforeExecJob(taskModel models.Task) (taskLogId int64) {
if taskModel.Multi == 0 && runInstance.has(taskModel.Id) { if taskModel.Multi == 0 && runInstance.has(taskModel.Id) {
createTaskLog(taskModel, models.Cancel) createTaskLog(taskModel, models.Cancel)
return return
} }
if taskModel.Multi == 0 { if taskModel.Multi == 0 {
runInstance.add(taskModel.Id) runInstance.add(taskModel.Id)
} }
taskLogId, err := createTaskLog(taskModel, models.Running) taskLogId, err := createTaskLog(taskModel, models.Running)
if err != nil { if err != nil {
logger.Error("任务开始执行#写入任务日志失败-", err) logger.Error("任务开始执行#写入任务日志失败-", err)
return return
} }
logger.Debugf("任务命令-%s", taskModel.Command) logger.Debugf("任务命令-%s", taskModel.Command)
return taskLogId return taskLogId
} }
// 任务执行后置操作 // 任务执行后置操作
func afterExecJob(taskModel models.Task, taskResult TaskResult, taskLogId int64) { func afterExecJob(taskModel models.Task, taskResult TaskResult, taskLogId int64) {
_, err := updateTaskLog(taskLogId, taskResult) _, err := updateTaskLog(taskLogId, taskResult)
if err != nil { if err != nil {
logger.Error("任务结束#更新任务日志失败-", err) logger.Error("任务结束#更新任务日志失败-", err)
} }
// 发送邮件 // 发送邮件
go SendNotification(taskModel, taskResult) go SendNotification(taskModel, taskResult)
// 执行依赖任务 // 执行依赖任务
go execDependencyTask(taskModel, taskResult) go execDependencyTask(taskModel, taskResult)
} }
// 执行依赖任务, 多个任务并发执行 // 执行依赖任务, 多个任务并发执行
func execDependencyTask(taskModel models.Task, taskResult TaskResult) { func execDependencyTask(taskModel models.Task, taskResult TaskResult) {
// 父任务才能执行子任务 // 父任务才能执行子任务
if taskModel.Level != models.TaskLevelParent { if taskModel.Level != models.TaskLevelParent {
return return
} }
// 是否存在子任务 // 是否存在子任务
dependencyTaskId := strings.TrimSpace(taskModel.DependencyTaskId) dependencyTaskId := strings.TrimSpace(taskModel.DependencyTaskId)
if dependencyTaskId == "" { if dependencyTaskId == "" {
return return
} }
// 父子任务关系为强依赖, 父任务执行失败, 不执行依赖任务 // 父子任务关系为强依赖, 父任务执行失败, 不执行依赖任务
if taskModel.DependencyStatus == models.TaskDependencyStatusStrong && taskResult.Err != nil { if taskModel.DependencyStatus == models.TaskDependencyStatusStrong && taskResult.Err != nil {
logger.Infof("父子任务为强依赖关系, 父任务执行失败, 不运行依赖任务#主任务ID-%d", taskModel.Id) logger.Infof("父子任务为强依赖关系, 父任务执行失败, 不运行依赖任务#主任务ID-%d", taskModel.Id)
return return
} }
// 获取子任务 // 获取子任务
model := new(models.Task) model := new(models.Task)
tasks , err := model.GetDependencyTaskList(dependencyTaskId) tasks, err := model.GetDependencyTaskList(dependencyTaskId)
if err != nil { if err != nil {
logger.Errorf("获取依赖任务失败#主任务ID-%d#%s", taskModel.Id, err.Error()) logger.Errorf("获取依赖任务失败#主任务ID-%d#%s", taskModel.Id, err.Error())
return return
} }
if len(tasks) == 0 { if len(tasks) == 0 {
logger.Errorf("依赖任务列表为空#主任务ID-%d", taskModel.Id) logger.Errorf("依赖任务列表为空#主任务ID-%d", taskModel.Id)
} }
serviceTask := new(Task) serviceTask := new(Task)
for _, task := range tasks { for _, task := range tasks {
task.Spec = fmt.Sprintf("依赖任务(主任务ID-%d)", taskModel.Id) task.Spec = fmt.Sprintf("依赖任务(主任务ID-%d)", taskModel.Id)
serviceTask.Run(task) serviceTask.Run(task)
} }
} }
// 发送任务结果通知 // 发送任务结果通知
func SendNotification(taskModel models.Task, taskResult TaskResult) { func SendNotification(taskModel models.Task, taskResult TaskResult) {
var statusName string var statusName string
// 未开启通知 // 未开启通知
if taskModel.NotifyStatus == 0 { if taskModel.NotifyStatus == 0 {
return return
} }
if taskModel.NotifyStatus == 1 && taskResult.Err == nil { if taskModel.NotifyStatus == 1 && taskResult.Err == nil {
// 执行失败才发送通知 // 执行失败才发送通知
return return
} }
if taskModel.NotifyReceiverId == "" { if taskModel.NotifyReceiverId == "" {
return return
} }
if taskResult.Err != nil { if taskResult.Err != nil {
statusName = "失败" statusName = "失败"
} else { } else {
statusName = "成功" statusName = "成功"
} }
// 发送通知 // 发送通知
msg := notify.Message{ msg := notify.Message{
"task_type": taskModel.NotifyType, "task_type": taskModel.NotifyType,
"task_receiver_id": taskModel.NotifyReceiverId, "task_receiver_id": taskModel.NotifyReceiverId,
"name": taskModel.Name, "name": taskModel.Name,
"output": taskResult.Result, "output": taskResult.Result,
"status": statusName, "status": statusName,
"taskId": taskModel.Id, "taskId": taskModel.Id,
}; }
notify.Push(msg) notify.Push(msg)
} }
// 执行具体任务 // 执行具体任务
func execJob(handler Handler, taskModel models.Task) TaskResult { func execJob(handler Handler, taskModel models.Task) TaskResult {
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
logger.Error("panic#service/task.go:execJob#", err) logger.Error("panic#service/task.go:execJob#", err)
} }
} () }()
if taskModel.Multi == 0 { if taskModel.Multi == 0 {
defer runInstance.done(taskModel.Id) defer runInstance.done(taskModel.Id)
} }
// 默认只运行任务一次 // 默认只运行任务一次
var execTimes int8 = 1 var execTimes int8 = 1
if (taskModel.RetryTimes > 0) { if taskModel.RetryTimes > 0 {
execTimes += taskModel.RetryTimes execTimes += taskModel.RetryTimes
} }
var i int8 = 0 var i int8 = 0
var output string var output string
var err error var err error
for i < execTimes { for i < execTimes {
output, err = handler.Run(taskModel) output, err = handler.Run(taskModel)
if err == nil { if err == nil {
return TaskResult{Result: output, Err: err, RetryTimes: i} return TaskResult{Result: output, Err: err, RetryTimes: i}
} }
i++ i++
if i < execTimes { if i < execTimes {
logger.Warnf("任务执行失败#任务id-%d#重试第%d次#输出-%s#错误-%s", taskModel.Id, i, output, err.Error()) logger.Warnf("任务执行失败#任务id-%d#重试第%d次#输出-%s#错误-%s", taskModel.Id, i, output, err.Error())
// 重试间隔时间每次递增1分钟 // 重试间隔时间每次递增1分钟
time.Sleep( time.Duration(i) * time.Minute) time.Sleep(time.Duration(i) * time.Minute)
} }
} }
return TaskResult{Result: output, Err: err, RetryTimes: taskModel.RetryTimes} return TaskResult{Result: output, Err: err, RetryTimes: taskModel.RetryTimes}
} }

View File

@ -14,12 +14,12 @@
package com 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 { func PowInt(x int, y int) int {
if y <= 0 { if y <= 0 {
return 1 return 1
} else { } else {
if y % 2 == 0 { if y%2 == 0 {
sqrt := PowInt(x, y/2) sqrt := PowInt(x, y/2)
return sqrt * sqrt return sqrt * sqrt
} else { } else {

View File

@ -124,7 +124,7 @@ type Options struct {
// Cookie value used to set and get token. // Cookie value used to set and get token.
Cookie string Cookie string
// Cookie path. // Cookie path.
CookiePath string CookiePath string
CookieHttpOnly bool CookieHttpOnly bool
// Key used for getting the unique ID per user. // Key used for getting the unique ID per user.
SessionKey string SessionKey string

View File

@ -1,217 +1,217 @@
// Copyright 2013 Beego Authors // Copyright 2013 Beego Authors
// Copyright 2014 The Macaron Authors // Copyright 2014 The Macaron Authors
// //
// Licensed under the Apache License, Version 2.0 (the "License"): you may // 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 // not use this file except in compliance with the License. You may obtain
// a copy of the License at // a copy of the License at
// //
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations // License for the specific language governing permissions and limitations
// under the License. // under the License.
package session package session
import ( import (
"container/list" "container/list"
"fmt" "fmt"
"sync" "sync"
"time" "time"
) )
// MemStore represents a in-memory session store implementation. // MemStore represents a in-memory session store implementation.
type MemStore struct { type MemStore struct {
sid string sid string
lock sync.RWMutex lock sync.RWMutex
data map[interface{}]interface{} data map[interface{}]interface{}
lastAccess time.Time lastAccess time.Time
} }
// NewMemStore creates and returns a memory session store. // NewMemStore creates and returns a memory session store.
func NewMemStore(sid string) *MemStore { func NewMemStore(sid string) *MemStore {
return &MemStore{ return &MemStore{
sid: sid, sid: sid,
data: make(map[interface{}]interface{}), data: make(map[interface{}]interface{}),
lastAccess: time.Now(), lastAccess: time.Now(),
} }
} }
// Set sets value to given key in session. // Set sets value to given key in session.
func (s *MemStore) Set(key, val interface{}) error { func (s *MemStore) Set(key, val interface{}) error {
s.lock.Lock() s.lock.Lock()
defer s.lock.Unlock() defer s.lock.Unlock()
s.data[key] = val s.data[key] = val
return nil return nil
} }
// Get gets value by given key in session. // Get gets value by given key in session.
func (s *MemStore) Get(key interface{}) interface{} { func (s *MemStore) Get(key interface{}) interface{} {
s.lock.RLock() s.lock.RLock()
defer s.lock.RUnlock() defer s.lock.RUnlock()
return s.data[key] return s.data[key]
} }
// Delete deletes a key from session. // Delete deletes a key from session.
func (s *MemStore) Delete(key interface{}) error { func (s *MemStore) Delete(key interface{}) error {
s.lock.Lock() s.lock.Lock()
defer s.lock.Unlock() defer s.lock.Unlock()
delete(s.data, key) delete(s.data, key)
return nil return nil
} }
// ID returns current session ID. // ID returns current session ID.
func (s *MemStore) ID() string { func (s *MemStore) ID() string {
return s.sid return s.sid
} }
// Release releases resource and save data to provider. // Release releases resource and save data to provider.
func (_ *MemStore) Release() error { func (_ *MemStore) Release() error {
return nil return nil
} }
// Flush deletes all session data. // Flush deletes all session data.
func (s *MemStore) Flush() error { func (s *MemStore) Flush() error {
s.lock.Lock() s.lock.Lock()
defer s.lock.Unlock() defer s.lock.Unlock()
s.data = make(map[interface{}]interface{}) s.data = make(map[interface{}]interface{})
return nil return nil
} }
// MemProvider represents a in-memory session provider implementation. // MemProvider represents a in-memory session provider implementation.
type MemProvider struct { type MemProvider struct {
lock sync.RWMutex lock sync.RWMutex
maxLifetime int64 maxLifetime int64
data map[string]*list.Element data map[string]*list.Element
// A priority list whose lastAccess newer gets higer priority. // A priority list whose lastAccess newer gets higer priority.
list *list.List list *list.List
} }
// Init initializes memory session provider. // Init initializes memory session provider.
func (p *MemProvider) Init(maxLifetime int64, _ string) error { func (p *MemProvider) Init(maxLifetime int64, _ string) error {
p.lock.Lock() p.lock.Lock()
p.maxLifetime = maxLifetime p.maxLifetime = maxLifetime
p.lock.Unlock() p.lock.Unlock()
return nil return nil
} }
// update expands time of session store by given ID. // update expands time of session store by given ID.
func (p *MemProvider) update(sid string) error { func (p *MemProvider) update(sid string) error {
p.lock.Lock() p.lock.Lock()
defer p.lock.Unlock() defer p.lock.Unlock()
if e, ok := p.data[sid]; ok { if e, ok := p.data[sid]; ok {
e.Value.(*MemStore).lastAccess = time.Now() e.Value.(*MemStore).lastAccess = time.Now()
p.list.MoveToFront(e) p.list.MoveToFront(e)
return nil return nil
} }
return nil return nil
} }
// Read returns raw session store by session ID. // Read returns raw session store by session ID.
func (p *MemProvider) Read(sid string) (_ RawStore, err error) { func (p *MemProvider) Read(sid string) (_ RawStore, err error) {
p.lock.RLock() p.lock.RLock()
e, ok := p.data[sid] e, ok := p.data[sid]
p.lock.RUnlock() p.lock.RUnlock()
if ok { if ok {
if err = p.update(sid); err != nil { if err = p.update(sid); err != nil {
return nil, err return nil, err
} }
return e.Value.(*MemStore), nil return e.Value.(*MemStore), nil
} }
// Create a new session. // Create a new session.
p.lock.Lock() p.lock.Lock()
defer p.lock.Unlock() defer p.lock.Unlock()
s := NewMemStore(sid) s := NewMemStore(sid)
p.data[sid] = p.list.PushBack(s) p.data[sid] = p.list.PushBack(s)
return s, nil return s, nil
} }
// Exist returns true if session with given ID exists. // Exist returns true if session with given ID exists.
func (p *MemProvider) Exist(sid string) bool { func (p *MemProvider) Exist(sid string) bool {
p.lock.RLock() p.lock.RLock()
defer p.lock.RUnlock() defer p.lock.RUnlock()
_, ok := p.data[sid] _, ok := p.data[sid]
return ok return ok
} }
// Destory deletes a session by session ID. // Destory deletes a session by session ID.
func (p *MemProvider) Destory(sid string) error { func (p *MemProvider) Destory(sid string) error {
p.lock.Lock() p.lock.Lock()
defer p.lock.Unlock() defer p.lock.Unlock()
e, ok := p.data[sid] e, ok := p.data[sid]
if !ok { if !ok {
return nil return nil
} }
p.list.Remove(e) p.list.Remove(e)
delete(p.data, sid) delete(p.data, sid)
return nil return nil
} }
// Regenerate regenerates a session store from old session ID to new one. // Regenerate regenerates a session store from old session ID to new one.
func (p *MemProvider) Regenerate(oldsid, sid string) (RawStore, error) { func (p *MemProvider) Regenerate(oldsid, sid string) (RawStore, error) {
if p.Exist(sid) { if p.Exist(sid) {
return nil, fmt.Errorf("new sid '%s' already exists", sid) return nil, fmt.Errorf("new sid '%s' already exists", sid)
} }
s, err := p.Read(oldsid) s, err := p.Read(oldsid)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err = p.Destory(oldsid); err != nil { if err = p.Destory(oldsid); err != nil {
return nil, err return nil, err
} }
s.(*MemStore).sid = sid s.(*MemStore).sid = sid
p.lock.Lock() p.lock.Lock()
defer p.lock.Unlock() defer p.lock.Unlock()
p.data[sid] = p.list.PushBack(s) p.data[sid] = p.list.PushBack(s)
return s, nil return s, nil
} }
// Count counts and returns number of sessions. // Count counts and returns number of sessions.
func (p *MemProvider) Count() int { func (p *MemProvider) Count() int {
return p.list.Len() return p.list.Len()
} }
// GC calls GC to clean expired sessions. // GC calls GC to clean expired sessions.
func (p *MemProvider) GC() { func (p *MemProvider) GC() {
p.lock.RLock() p.lock.RLock()
for { for {
// No session in the list. // No session in the list.
e := p.list.Back() e := p.list.Back()
if e == nil { if e == nil {
break break
} }
if (e.Value.(*MemStore).lastAccess.Unix() + p.maxLifetime) < time.Now().Unix() { if (e.Value.(*MemStore).lastAccess.Unix() + p.maxLifetime) < time.Now().Unix() {
p.lock.RUnlock() p.lock.RUnlock()
p.lock.Lock() p.lock.Lock()
p.list.Remove(e) p.list.Remove(e)
delete(p.data, e.Value.(*MemStore).sid) delete(p.data, e.Value.(*MemStore).sid)
p.lock.Unlock() p.lock.Unlock()
p.lock.RLock() p.lock.RLock()
} else { } else {
break break
} }
} }
p.lock.RUnlock() p.lock.RUnlock()
} }
func init() { func init() {
Register("memory", &MemProvider{list: list.New(), data: make(map[string]*list.Element)}) Register("memory", &MemProvider{list: list.New(), data: make(map[string]*list.Element)})
} }

View File

@ -1,11 +1,11 @@
package core package core
import ( import (
"bytes"
"encoding/gob"
"errors" "errors"
"fmt" "fmt"
"time" "time"
"bytes"
"encoding/gob"
) )
const ( const (
@ -55,7 +55,6 @@ func encodeIds(ids []PK) (string, error) {
return buf.String(), err return buf.String(), err
} }
func decodeIds(s string) ([]PK, error) { func decodeIds(s string) ([]PK, error) {
pks := make([]PK, 0) pks := make([]PK, 0)

View File

@ -90,7 +90,7 @@ type FuncJob func()
func (f FuncJob) Run() { f() } func (f FuncJob) Run() { f() }
// AddFunc adds a func to the Cron to be run on the given schedule. // 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) return c.AddJob(spec, FuncJob(cmd), name)
} }

View File

@ -1,12 +1,12 @@
package cron package cron
import ( import (
"fmt"
"log" "log"
"math" "math"
"strconv" "strconv"
"strings" "strings"
"time" "time"
"fmt"
) )
// Parse returns a new crontab schedule representing the given spec. // 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 // parseDescriptor returns a pre-defined schedule for the expression, or panics
// if none matches. // if none matches.
func parseDescriptor(spec string) (Schedule,error) { func parseDescriptor(spec string) (Schedule, error) {
switch spec { switch spec {
case "@yearly", "@annually": case "@yearly", "@annually":
return &SpecSchedule{ return &SpecSchedule{
@ -217,10 +217,10 @@ func parseDescriptor(spec string) (Schedule,error) {
if strings.HasPrefix(spec, every) { if strings.HasPrefix(spec, every) {
duration, err := time.ParseDuration(spec[len(every):]) duration, err := time.ParseDuration(spec[len(every):])
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to parse duration %s: %s", spec, err) return nil, fmt.Errorf("Failed to parse duration %s: %s", spec, err)
} }
return Every(duration),nil return Every(duration), nil
} }
return nil, fmt.Errorf("Unrecognized descriptor: %s", spec) return nil, fmt.Errorf("Unrecognized descriptor: %s", spec)
} }

View File

@ -383,8 +383,8 @@ func init() {
// 4253 and Oakley Group 2 in RFC 2409. // 4253 and Oakley Group 2 in RFC 2409.
p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16) p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16)
kexAlgoMap[kexAlgoDH1SHA1] = &dhGroup{ kexAlgoMap[kexAlgoDH1SHA1] = &dhGroup{
g: new(big.Int).SetInt64(2), g: new(big.Int).SetInt64(2),
p: p, p: p,
pMinus1: new(big.Int).Sub(p, bigOne), pMinus1: new(big.Int).Sub(p, bigOne),
} }
@ -393,8 +393,8 @@ func init() {
p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16) p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16)
kexAlgoMap[kexAlgoDH14SHA1] = &dhGroup{ kexAlgoMap[kexAlgoDH14SHA1] = &dhGroup{
g: new(big.Int).SetInt64(2), g: new(big.Int).SetInt64(2),
p: p, p: p,
pMinus1: new(big.Int).Sub(p, bigOne), pMinus1: new(big.Int).Sub(p, bigOne),
} }