mirror of https://github.com/ouqiang/gocron
tab替换为空格
parent
702db588f2
commit
d7461a794f
102
cmd/web.go
102
cmd/web.go
|
@ -1,13 +1,13 @@
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/go-macaron/csrf"
|
"github.com/go-macaron/csrf"
|
||||||
"github.com/go-macaron/gzip"
|
"github.com/go-macaron/gzip"
|
||||||
"github.com/go-macaron/session"
|
"github.com/go-macaron/session"
|
||||||
"github.com/ouqiang/cron-scheduler/modules/app"
|
"github.com/ouqiang/cron-scheduler/modules/app"
|
||||||
"github.com/ouqiang/cron-scheduler/routers"
|
"github.com/ouqiang/cron-scheduler/routers"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
"gopkg.in/macaron.v1"
|
"gopkg.in/macaron.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// web服务器默认端口
|
// web服务器默认端口
|
||||||
|
@ -17,60 +17,60 @@ const DefaultPort = 5920
|
||||||
const StaticDir = "public"
|
const StaticDir = "public"
|
||||||
|
|
||||||
var CmdWeb = cli.Command{
|
var CmdWeb = cli.Command{
|
||||||
Name: "server",
|
Name: "server",
|
||||||
Usage: "start scheduler web server",
|
Usage: "start scheduler web server",
|
||||||
Action: run,
|
Action: run,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
cli.IntFlag{
|
cli.IntFlag{
|
||||||
Name: "port,p",
|
Name: "port,p",
|
||||||
Value: DefaultPort,
|
Value: DefaultPort,
|
||||||
Usage: "bind port number",
|
Usage: "bind port number",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func run(ctx *cli.Context) {
|
func run(ctx *cli.Context) {
|
||||||
app.InitEnv()
|
app.InitEnv()
|
||||||
m := macaron.Classic()
|
m := macaron.Classic()
|
||||||
// 注册路由
|
// 注册路由
|
||||||
routers.Register(m)
|
routers.Register(m)
|
||||||
// 注册中间件
|
// 注册中间件
|
||||||
registerMiddleware(m)
|
registerMiddleware(m)
|
||||||
port := parsePort(ctx)
|
port := parsePort(ctx)
|
||||||
m.Run(port)
|
m.Run(port)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 中间件注册
|
// 中间件注册
|
||||||
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())
|
||||||
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,
|
||||||
}))
|
}))
|
||||||
m.Use(session.Sessioner())
|
m.Use(session.Sessioner())
|
||||||
m.Use(csrf.Csrfer())
|
m.Use(csrf.Csrfer())
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析端口
|
// 解析端口
|
||||||
func parsePort(ctx *cli.Context) int {
|
func parsePort(ctx *cli.Context) int {
|
||||||
var port int
|
var port int
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
2
main.go
2
main.go
|
@ -1,7 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
/*--------------------------------------------------------
|
/*--------------------------------------------------------
|
||||||
定时任务调度
|
定时任务调度
|
||||||
兼容Linux crontab时间格式语法,最小粒度可精确到每秒
|
兼容Linux crontab时间格式语法,最小粒度可精确到每秒
|
||||||
支持通过HTTP、SSH协议任务执行
|
支持通过HTTP、SSH协议任务执行
|
||||||
--------------------------------------------------------*/
|
--------------------------------------------------------*/
|
||||||
|
|
|
@ -2,66 +2,66 @@ package models
|
||||||
|
|
||||||
// 主机
|
// 主机
|
||||||
type Host struct {
|
type Host struct {
|
||||||
Id int16 `xorm:"smallint pk autoincr"`
|
Id int16 `xorm:"smallint pk autoincr"`
|
||||||
Name string `xorm:"varchar(128) notnull"` // 主机名称
|
Name string `xorm:"varchar(128) notnull"` // 主机名称
|
||||||
Alias string `xorm:"varchar(32) notnull default '' "` // 主机别名
|
Alias string `xorm:"varchar(32) notnull default '' "` // 主机别名
|
||||||
Username string `xorm:"varchar(32) notnull default '' "` // ssh 用户名
|
Username string `xorm:"varchar(32) notnull default '' "` // ssh 用户名
|
||||||
Password string `xorm:"varchar(64) notnull default ''"` // ssh 密码
|
Password string `xorm:"varchar(64) notnull default ''"` // ssh 密码
|
||||||
Port int `xorm:"notnull default 22"` // 主机端口
|
Port int `xorm:"notnull default 22"` // 主机端口
|
||||||
LoginType LoginType `xorm:"tinyint notnull default 1"` // ssh登录方式 1:公钥认证 2:账号密码
|
LoginType LoginType `xorm:"tinyint notnull default 1"` // ssh登录方式 1:公钥认证 2:账号密码
|
||||||
Remark string `xorm:"varchar(512) notnull default '' "` // 备注
|
Remark string `xorm:"varchar(512) notnull default '' "` // 备注
|
||||||
Page int `xorm:"-"`
|
Page int `xorm:"-"`
|
||||||
PageSize int `xorm:"-"`
|
PageSize int `xorm:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LoginType int8
|
type LoginType int8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
PublicKey = 1
|
PublicKey = 1
|
||||||
UserPassword = 2
|
UserPassword = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
// 新增
|
// 新增
|
||||||
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) 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(host)
|
return Db.Id(id).Delete(host)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (host *Host) List() ([]Host, error) {
|
func (host *Host) List() ([]Host, error) {
|
||||||
host.parsePageAndPageSize()
|
host.parsePageAndPageSize()
|
||||||
list := make([]Host, 0)
|
list := make([]Host, 0)
|
||||||
err := Db.Desc("id").Limit(host.PageSize, host.pageLimitOffset()).Find(&list)
|
err := Db.Desc("id").Limit(host.PageSize, host.pageLimitOffset()).Find(&list)
|
||||||
|
|
||||||
return list, err
|
return list, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (host *Host) Total() (int64, error) {
|
func (host *Host) Total() (int64, error) {
|
||||||
return Db.Count(host)
|
return Db.Count(host)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (host *Host) parsePageAndPageSize() {
|
func (host *Host) parsePageAndPageSize() {
|
||||||
if host.Page <= 0 {
|
if host.Page <= 0 {
|
||||||
host.Page = Page
|
host.Page = Page
|
||||||
}
|
}
|
||||||
if host.PageSize >= 0 || host.PageSize > MaxPageSize {
|
if host.PageSize >= 0 || host.PageSize > MaxPageSize {
|
||||||
host.PageSize = PageSize
|
host.PageSize = PageSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (host *Host) pageLimitOffset() int {
|
func (host *Host) pageLimitOffset() int {
|
||||||
return (host.Page - 1) * host.PageSize
|
return (host.Page - 1) * host.PageSize
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,25 +7,25 @@ import "errors"
|
||||||
type Migration struct{}
|
type Migration struct{}
|
||||||
|
|
||||||
func (migration *Migration) Exec(dbName string) error {
|
func (migration *Migration) Exec(dbName string) error {
|
||||||
if !isDatabaseExist(dbName) {
|
if !isDatabaseExist(dbName) {
|
||||||
return errors.New("数据库不存在")
|
return errors.New("数据库不存在")
|
||||||
}
|
}
|
||||||
tables := []interface{}{
|
tables := []interface{}{
|
||||||
&User{}, &Task{}, &TaskLog{}, &Host{},
|
&User{}, &Task{}, &TaskLog{}, &Host{},
|
||||||
}
|
}
|
||||||
for _, table := range tables {
|
for _, table := range tables {
|
||||||
err := Db.Sync2(table)
|
err := Db.Sync2(table)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
126
models/model.go
126
models/model.go
|
@ -1,13 +1,13 @@
|
||||||
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"
|
||||||
"github.com/ouqiang/cron-scheduler/modules/setting"
|
"github.com/ouqiang/cron-scheduler/modules/setting"
|
||||||
"gopkg.in/macaron.v1"
|
"gopkg.in/macaron.v1"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Status int8
|
type Status int8
|
||||||
|
@ -16,78 +16,78 @@ type CommonMap map[string]interface{}
|
||||||
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 // 完成
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Page = 1 // 当前页数
|
Page = 1 // 当前页数
|
||||||
PageSize = 20 // 每页多少条数据
|
PageSize = 20 // 每页多少条数据
|
||||||
MaxPageSize = 100000 // 每次最多取多少条
|
MaxPageSize = 100000 // 每次最多取多少条
|
||||||
)
|
)
|
||||||
|
|
||||||
// 创建Db
|
// 创建Db
|
||||||
func CreateDb(configFile string) *xorm.Engine {
|
func CreateDb(configFile string) *xorm.Engine {
|
||||||
config := getDbConfig(configFile)
|
config := getDbConfig(configFile)
|
||||||
dsn := getDbEngineDSN(config["engine"], config)
|
dsn := getDbEngineDSN(config["engine"], config)
|
||||||
engine, err := xorm.NewEngine(config["engine"], dsn)
|
engine, err := xorm.NewEngine(config["engine"], dsn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
if config["prefix"] != "" {
|
if config["prefix"] != "" {
|
||||||
// 设置表前缀
|
// 设置表前缀
|
||||||
mapper := core.NewPrefixMapper(core.SnakeMapper{}, config["prefix"])
|
mapper := core.NewPrefixMapper(core.SnakeMapper{}, config["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)
|
||||||
}
|
}
|
||||||
|
|
||||||
return engine
|
return engine
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取数据库引擎DSN mysql,sqlite
|
// 获取数据库引擎DSN mysql,sqlite
|
||||||
func getDbEngineDSN(engine string, config map[string]string) string {
|
func getDbEngineDSN(engine string, config map[string]string) string {
|
||||||
engine = strings.ToLower(engine)
|
engine = strings.ToLower(engine)
|
||||||
var dsn string = ""
|
var dsn string = ""
|
||||||
switch engine {
|
switch engine {
|
||||||
case "mysql":
|
case "mysql":
|
||||||
dsn = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s",
|
dsn = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s",
|
||||||
config["user"],
|
config["user"],
|
||||||
config["password"],
|
config["password"],
|
||||||
config["host"],
|
config["host"],
|
||||||
config["port"],
|
config["port"],
|
||||||
config["database"],
|
config["database"],
|
||||||
config["charset"])
|
config["charset"])
|
||||||
}
|
}
|
||||||
|
|
||||||
return dsn
|
return dsn
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取数据库配置
|
// 获取数据库配置
|
||||||
func getDbConfig(configFile string) map[string]string {
|
func getDbConfig(configFile string) map[string]string {
|
||||||
config, err := setting.Read(configFile)
|
config, err := setting.Read(configFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
section := config.Section("db")
|
section := config.Section("db")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
var db map[string]string = make(map[string]string)
|
var db map[string]string = make(map[string]string)
|
||||||
db["user"] = section.Key("user").String()
|
db["user"] = section.Key("user").String()
|
||||||
db["password"] = section.Key("password").String()
|
db["password"] = section.Key("password").String()
|
||||||
db["host"] = section.Key("host").String()
|
db["host"] = section.Key("host").String()
|
||||||
db["port"] = section.Key("port").String()
|
db["port"] = section.Key("port").String()
|
||||||
db["database"] = section.Key("database").String()
|
db["database"] = section.Key("database").String()
|
||||||
db["charset"] = section.Key("charset").String()
|
db["charset"] = section.Key("charset").String()
|
||||||
db["prefix"] = section.Key("prefix").String()
|
db["prefix"] = section.Key("prefix").String()
|
||||||
db["engine"] = section.Key("engine").String()
|
db["engine"] = section.Key("engine").String()
|
||||||
|
|
||||||
return db
|
return db
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Protocol int8
|
type Protocol int8
|
||||||
|
@ -9,96 +9,96 @@ type Protocol int8
|
||||||
type TaskType int8
|
type TaskType int8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
HTTP Protocol = 1
|
HTTP Protocol = 1
|
||||||
SSHCommand Protocol = 2
|
SSHCommand Protocol = 2
|
||||||
SSHScript Protocol = 3
|
SSHScript Protocol = 3
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Timing TaskType = 1
|
Timing TaskType = 1
|
||||||
Delay TaskType = 2
|
Delay TaskType = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
// 任务
|
// 任务
|
||||||
type Task struct {
|
type Task struct {
|
||||||
Id int `xorm:"int pk autoincr"`
|
Id int `xorm:"int pk autoincr"`
|
||||||
Name string `xorm:"varchar(64) notnull"` // 任务名称
|
Name string `xorm:"varchar(64) notnull"` // 任务名称
|
||||||
Spec string `xorm:"varchar(64) notnull"` // crontab 时间格式
|
Spec string `xorm:"varchar(64) notnull"` // crontab 时间格式
|
||||||
Protocol Protocol `xorm:"tinyint notnull"` // 协议 1:http 2:ssh-command 3:ssh-script
|
Protocol Protocol `xorm:"tinyint notnull"` // 协议 1:http 2:ssh-command 3:ssh-script
|
||||||
Type TaskType `xorm:"tinyint notnull default 1"` // 任务类型 1: 定时任务 2: 延时任务
|
Type TaskType `xorm:"tinyint notnull default 1"` // 任务类型 1: 定时任务 2: 延时任务
|
||||||
Command string `xorm:"varchar(512) notnull"` // URL地址或shell命令
|
Command string `xorm:"varchar(512) notnull"` // URL地址或shell命令
|
||||||
Timeout int `xorm:"mediumint notnull default 0"` // 任务执行超时时间(单位秒),0不限制
|
Timeout int `xorm:"mediumint notnull default 0"` // 任务执行超时时间(单位秒),0不限制
|
||||||
Delay int `xorm:"int notnull default 0"` // 延时任务,延时时间(单位秒)
|
Delay int `xorm:"int notnull default 0"` // 延时任务,延时时间(单位秒)
|
||||||
SshHosts string `xorm:"varchar(512) notnull defalut '' "` // SSH主机名, host id,逗号分隔
|
SshHosts string `xorm:"varchar(512) notnull defalut '' "` // SSH主机名, host id,逗号分隔
|
||||||
Remark string `xorm:"varchar(512) notnull default ''"` // 备注
|
Remark string `xorm:"varchar(512) notnull default ''"` // 备注
|
||||||
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"` // 删除时间
|
||||||
Status Status `xorm:"tinyint notnull default 1"` // 状态 1:正常 0:停止
|
Status Status `xorm:"tinyint notnull default 1"` // 状态 1:正常 0:停止
|
||||||
Page int `xorm:"-"`
|
Page int `xorm:"-"`
|
||||||
PageSize int `xorm:"-"`
|
PageSize int `xorm:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增
|
// 新增
|
||||||
func (task *Task) Create() (insertId int, err error) {
|
func (task *Task) Create() (insertId int, err error) {
|
||||||
task.Status = Enabled
|
task.Status = Enabled
|
||||||
|
|
||||||
_, 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) 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) {
|
||||||
task.parsePageAndPageSize()
|
task.parsePageAndPageSize()
|
||||||
list := make([]Task, 0)
|
list := make([]Task, 0)
|
||||||
err := Db.Where("status = ?", Enabled).Desc("id").Find(&list)
|
err := Db.Where("status = ?", Enabled).Desc("id").Find(&list)
|
||||||
|
|
||||||
return list, err
|
return list, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (task *Task) List() ([]Task, error) {
|
func (task *Task) List() ([]Task, error) {
|
||||||
task.parsePageAndPageSize()
|
task.parsePageAndPageSize()
|
||||||
list := make([]Task, 0)
|
list := make([]Task, 0)
|
||||||
err := Db.Desc("id").Limit(task.PageSize, task.pageLimitOffset()).Find(&list)
|
err := Db.Desc("id").Limit(task.PageSize, task.pageLimitOffset()).Find(&list)
|
||||||
|
|
||||||
return list, err
|
return list, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (taskLog *TaskLog) Total() (int64, error) {
|
func (taskLog *TaskLog) Total() (int64, error) {
|
||||||
return Db.Count(taskLog)
|
return Db.Count(taskLog)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (taskLog *TaskLog) parsePageAndPageSize() {
|
func (taskLog *TaskLog) parsePageAndPageSize() {
|
||||||
if taskLog.Page <= 0 {
|
if taskLog.Page <= 0 {
|
||||||
taskLog.Page = Page
|
taskLog.Page = Page
|
||||||
}
|
}
|
||||||
if taskLog.PageSize >= 0 || taskLog.PageSize > MaxPageSize {
|
if taskLog.PageSize >= 0 || taskLog.PageSize > MaxPageSize {
|
||||||
taskLog.PageSize = PageSize
|
taskLog.PageSize = PageSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (taskLog *TaskLog) pageLimitOffset() int {
|
func (taskLog *TaskLog) pageLimitOffset() int {
|
||||||
return (taskLog.Page - 1) * taskLog.PageSize
|
return (taskLog.Page - 1) * taskLog.PageSize
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,62 +1,62 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 任务执行日志
|
// 任务执行日志
|
||||||
type TaskLog struct {
|
type TaskLog struct {
|
||||||
Id int `xorm:"int pk autoincr"`
|
Id int `xorm:"int pk autoincr"`
|
||||||
TaskId int `xorm:"int not null"` // 任务ID
|
TaskId int `xorm:"int not null"` // 任务ID
|
||||||
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 default 1"` // 状态 1:执行中 2:执行完毕 0:执行失败
|
Status Status `xorm:"tinyint notnull default 1"` // 状态 1:执行中 2:执行完毕 0:执行失败
|
||||||
Result string `xorm:"varchar(65535) notnull defalut '' "` // 执行结果
|
Result string `xorm:"varchar(65535) notnull defalut '' "` // 执行结果
|
||||||
Page int `xorm:"-"`
|
Page int `xorm:"-"`
|
||||||
PageSize int `xorm:"-"`
|
PageSize int `xorm:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (taskLog *TaskLog) Create() (insertId int, err error) {
|
func (taskLog *TaskLog) Create() (insertId int, err error) {
|
||||||
taskLog.Status = Running
|
taskLog.Status = Running
|
||||||
|
|
||||||
_, 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 int, data CommonMap) (int64, error) {
|
func (taskLog *TaskLog) Update(id int, data CommonMap) (int64, error) {
|
||||||
return Db.Table(taskLog).ID(id).Update(data)
|
return Db.Table(taskLog).ID(id).Update(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (taskLog *TaskLog) setStatus(id int, status Status) (int64, error) {
|
func (taskLog *TaskLog) setStatus(id int, status Status) (int64, error) {
|
||||||
return taskLog.Update(id, CommonMap{"status": status})
|
return taskLog.Update(id, CommonMap{"status": status})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (taskLog *TaskLog) List() ([]TaskLog, error) {
|
func (taskLog *TaskLog) List() ([]TaskLog, error) {
|
||||||
taskLog.parsePageAndPageSize()
|
taskLog.parsePageAndPageSize()
|
||||||
list := make([]TaskLog, 0)
|
list := make([]TaskLog, 0)
|
||||||
err := Db.Desc("id").Limit(taskLog.PageSize, taskLog.pageLimitOffset()).Find(&list)
|
err := Db.Desc("id").Limit(taskLog.PageSize, taskLog.pageLimitOffset()).Find(&list)
|
||||||
|
|
||||||
return list, err
|
return list, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (task *Task) Total() (int64, error) {
|
func (task *Task) Total() (int64, error) {
|
||||||
return Db.Count(task)
|
return Db.Count(task)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (task *Task) parsePageAndPageSize() {
|
func (task *Task) parsePageAndPageSize() {
|
||||||
if task.Page <= 0 {
|
if task.Page <= 0 {
|
||||||
task.Page = Page
|
task.Page = Page
|
||||||
}
|
}
|
||||||
if task.PageSize >= 0 || task.PageSize > MaxPageSize {
|
if task.PageSize >= 0 || task.PageSize > MaxPageSize {
|
||||||
task.PageSize = PageSize
|
task.PageSize = PageSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (task *Task) pageLimitOffset() int {
|
func (task *Task) pageLimitOffset() int {
|
||||||
return (task.Page - 1) * task.PageSize
|
return (task.Page - 1) * task.PageSize
|
||||||
}
|
}
|
||||||
|
|
104
models/user.go
104
models/user.go
|
@ -1,118 +1,118 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/ouqiang/cron-scheduler/modules/utils"
|
"github.com/ouqiang/cron-scheduler/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:禁用
|
||||||
Page int `xorm:"-"`
|
Page int `xorm:"-"`
|
||||||
PageSize int `xorm:"-"`
|
PageSize int `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) 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) {
|
||||||
user.parsePageAndPageSize()
|
user.parsePageAndPageSize()
|
||||||
list := make([]User, 0)
|
list := make([]User, 0)
|
||||||
err := Db.Desc("id").Limit(user.PageSize, user.pageLimitOffset()).Find(&list)
|
err := Db.Desc("id").Limit(user.PageSize, user.pageLimitOffset()).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) parsePageAndPageSize() {
|
func (user *User) parsePageAndPageSize() {
|
||||||
if user.Page <= 0 {
|
if user.Page <= 0 {
|
||||||
user.Page = Page
|
user.Page = Page
|
||||||
}
|
}
|
||||||
if user.PageSize >= 0 || user.PageSize > MaxPageSize {
|
if user.PageSize >= 0 || user.PageSize > MaxPageSize {
|
||||||
user.PageSize = PageSize
|
user.PageSize = PageSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) pageLimitOffset() int {
|
func (user *User) pageLimitOffset() int {
|
||||||
return (user.Page - 1) * user.PageSize
|
return (user.Page - 1) * user.PageSize
|
||||||
}
|
}
|
||||||
|
|
||||||
// 密码加密
|
// 密码加密
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,8 @@ package ansible
|
||||||
// ansible ad-hoc 命令封装
|
// ansible ad-hoc 命令封装
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/ouqiang/cron-scheduler/modules/utils"
|
"github.com/ouqiang/cron-scheduler/modules/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -15,30 +15,30 @@ import (
|
||||||
* args 传递给module的参数
|
* args 传递给module的参数
|
||||||
*/
|
*/
|
||||||
func ExecCommand(hosts string, hostFile string, module string, args ...string) (output string, err error) {
|
func ExecCommand(hosts string, hostFile string, module string, args ...string) (output string, err error) {
|
||||||
if hosts == "" || hostFile == "" || module == "" {
|
if hosts == "" || hostFile == "" || module == "" {
|
||||||
err = errors.New("参数不完整")
|
err = errors.New("参数不完整")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
commandArgs := []string{hosts, "-i", hostFile, "-m", module}
|
commandArgs := []string{hosts, "-i", hostFile, "-m", module}
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
commandArgs = append(commandArgs, args...)
|
commandArgs = append(commandArgs, args...)
|
||||||
}
|
}
|
||||||
output, err = utils.ExecShell("ansible", commandArgs...)
|
output, err = utils.ExecShell("ansible", commandArgs...)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 执行shell命令
|
// 执行shell命令
|
||||||
func Shell(hosts string, hostFile string, args ...string) (output string, err error) {
|
func Shell(hosts string, hostFile string, args ...string) (output string, err error) {
|
||||||
return ExecCommand(hosts, hostFile, "shell", args...)
|
return ExecCommand(hosts, hostFile, "shell", args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 复制本地脚本到远程执行
|
// 复制本地脚本到远程执行
|
||||||
func Script(hosts string, hostFile string, args ...string) (output string, err error) {
|
func Script(hosts string, hostFile string, args ...string) (output string, err error) {
|
||||||
return ExecCommand(hosts, hostFile, "script", args...)
|
return ExecCommand(hosts, hostFile, "script", args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 测试主机是否可通
|
// 测试主机是否可通
|
||||||
func Ping(hosts string, hostFile string) (output string, err error) {
|
func Ping(hosts string, hostFile string) (output string, err error) {
|
||||||
return ExecCommand(hosts, hostFile, "ping")
|
return ExecCommand(hosts, hostFile, "ping")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,67 +1,67 @@
|
||||||
package ansible
|
package ansible
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"github.com/ouqiang/cron-scheduler/models"
|
"github.com/ouqiang/cron-scheduler/models"
|
||||||
"github.com/ouqiang/cron-scheduler/modules/utils"
|
"github.com/ouqiang/cron-scheduler/modules/utils"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 主机名
|
// 主机名
|
||||||
var DefaultHosts *Hosts
|
var DefaultHosts *Hosts
|
||||||
|
|
||||||
type Hosts struct {
|
type Hosts struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
filename string
|
filename string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHosts(hostFilename string) *Hosts {
|
func NewHosts(hostFilename string) *Hosts {
|
||||||
h := &Hosts{sync.RWMutex{}, hostFilename}
|
h := &Hosts{sync.RWMutex{}, hostFilename}
|
||||||
h.Write()
|
h.Write()
|
||||||
|
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取hosts文件名
|
// 获取hosts文件名
|
||||||
func (h *Hosts) GetFilename() string {
|
func (h *Hosts) GetFilename() string {
|
||||||
h.RLock()
|
h.RLock()
|
||||||
defer h.RUnlock()
|
defer h.RUnlock()
|
||||||
|
|
||||||
return h.filename
|
return h.filename
|
||||||
}
|
}
|
||||||
|
|
||||||
// 写入hosts
|
// 写入hosts
|
||||||
func (h *Hosts) Write() {
|
func (h *Hosts) Write() {
|
||||||
host := new(models.Host)
|
host := new(models.Host)
|
||||||
hostModels, err := host.List()
|
hostModels, err := host.List()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.RecordLog(err)
|
utils.RecordLog(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(hostModels) == 0 {
|
if len(hostModels) == 0 {
|
||||||
utils.RecordLog("hosts内容为空")
|
utils.RecordLog("hosts内容为空")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
buffer := bytes.Buffer{}
|
buffer := bytes.Buffer{}
|
||||||
for _, hostModel := range hostModels {
|
for _, hostModel := range hostModels {
|
||||||
buffer.WriteString(strconv.Itoa(int(hostModel.Id)))
|
buffer.WriteString(strconv.Itoa(int(hostModel.Id)))
|
||||||
buffer.WriteString(" ansible_ssh_host=")
|
buffer.WriteString(" ansible_ssh_host=")
|
||||||
buffer.WriteString(hostModel.Name)
|
buffer.WriteString(hostModel.Name)
|
||||||
buffer.WriteString(" ansible_ssh_port=")
|
buffer.WriteString(" ansible_ssh_port=")
|
||||||
buffer.WriteString(strconv.Itoa(hostModel.Port))
|
buffer.WriteString(strconv.Itoa(hostModel.Port))
|
||||||
buffer.WriteString(" ansible_ssh_user=")
|
buffer.WriteString(" ansible_ssh_user=")
|
||||||
buffer.WriteString(hostModel.Username)
|
buffer.WriteString(hostModel.Username)
|
||||||
if hostModel.LoginType != models.PublicKey && hostModel.Password != "" {
|
if hostModel.LoginType != models.PublicKey && hostModel.Password != "" {
|
||||||
buffer.WriteString(" ansible_ssh_pass=")
|
buffer.WriteString(" ansible_ssh_pass=")
|
||||||
buffer.WriteString(hostModel.Password)
|
buffer.WriteString(hostModel.Password)
|
||||||
}
|
}
|
||||||
buffer.WriteString("\n")
|
buffer.WriteString("\n")
|
||||||
}
|
}
|
||||||
h.Lock()
|
h.Lock()
|
||||||
defer h.Unlock()
|
defer h.Unlock()
|
||||||
err = ioutil.WriteFile(h.filename, buffer.Bytes(), 0644)
|
err = ioutil.WriteFile(h.filename, buffer.Bytes(), 0644)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,108 +1,108 @@
|
||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/ouqiang/cron-scheduler/models"
|
"github.com/ouqiang/cron-scheduler/models"
|
||||||
"github.com/ouqiang/cron-scheduler/modules/ansible"
|
"github.com/ouqiang/cron-scheduler/modules/ansible"
|
||||||
"github.com/ouqiang/cron-scheduler/modules/crontask"
|
"github.com/ouqiang/cron-scheduler/modules/crontask"
|
||||||
"github.com/ouqiang/cron-scheduler/modules/utils"
|
"github.com/ouqiang/cron-scheduler/modules/utils"
|
||||||
"github.com/ouqiang/cron-scheduler/service"
|
"github.com/ouqiang/cron-scheduler/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
AppDir string // 应用根目录
|
AppDir string // 应用根目录
|
||||||
ConfDir string // 配置目录
|
ConfDir string // 配置目录
|
||||||
LogDir string // 日志目录
|
LogDir string // 日志目录
|
||||||
DataDir string // 数据目录,存放session文件等
|
DataDir string // 数据目录,存放session文件等
|
||||||
AppConfig string // 应用配置文件
|
AppConfig string // 应用配置文件
|
||||||
AnsibleHosts string // ansible hosts文件
|
AnsibleHosts string // ansible hosts文件
|
||||||
Installed bool // 应用是否安装过
|
Installed bool // 应用是否安装过
|
||||||
)
|
)
|
||||||
|
|
||||||
func InitEnv() {
|
func InitEnv() {
|
||||||
CheckEnv()
|
CheckEnv()
|
||||||
wd, err := os.Getwd()
|
wd, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(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"
|
||||||
AnsibleHosts = ConfDir + "/ansible_hosts.ini"
|
AnsibleHosts = ConfDir + "/ansible_hosts.ini"
|
||||||
checkDirExists(ConfDir, LogDir, DataDir)
|
checkDirExists(ConfDir, LogDir, DataDir)
|
||||||
// ansible配置文件目录
|
// ansible配置文件目录
|
||||||
os.Setenv("ANSIBLE_CONFIG", ConfDir)
|
os.Setenv("ANSIBLE_CONFIG", ConfDir)
|
||||||
Installed = IsInstalled()
|
Installed = IsInstalled()
|
||||||
if Installed {
|
if Installed {
|
||||||
InitDb()
|
InitDb()
|
||||||
InitResource()
|
InitResource()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断应用是否安装过
|
// 判断应用是否安装过
|
||||||
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 CheckEnv() {
|
func CheckEnv() {
|
||||||
// ansible不支持安装在windows上, windows只能作为被控机
|
// ansible不支持安装在windows上, windows只能作为被控机
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
panic("不支持在windows上运行")
|
panic("不支持在windows上运行")
|
||||||
}
|
}
|
||||||
_, err := utils.ExecShell("ansible", "--version")
|
_, err := utils.ExecShell("ansible", "--version")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
_, err = utils.ExecShell("ansible-playbook", "--version")
|
_, err = utils.ExecShell("ansible-playbook", "--version")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建安装锁文件
|
// 创建安装锁文件
|
||||||
func CreateInstallLock() error {
|
func CreateInstallLock() error {
|
||||||
_, err := os.Create(ConfDir + "/install.lock")
|
_, err := os.Create(ConfDir + "/install.lock")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.RecordLog("创建安装锁文件失败")
|
utils.RecordLog("创建安装锁文件失败")
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化资源
|
// 初始化资源
|
||||||
func InitResource() {
|
func InitResource() {
|
||||||
// 初始化ansible Hosts
|
// 初始化ansible Hosts
|
||||||
ansible.DefaultHosts = ansible.NewHosts(AnsibleHosts)
|
ansible.DefaultHosts = ansible.NewHosts(AnsibleHosts)
|
||||||
// 初始化定时任务
|
// 初始化定时任务
|
||||||
crontask.DefaultCronTask = crontask.NewCronTask()
|
crontask.DefaultCronTask = crontask.NewCronTask()
|
||||||
serviceTask := new(service.Task)
|
serviceTask := new(service.Task)
|
||||||
serviceTask.Initialize()
|
serviceTask.Initialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化DB
|
// 初始化DB
|
||||||
func InitDb() {
|
func InitDb() {
|
||||||
models.Db = models.CreateDb(AppConfig)
|
models.Db = models.CreateDb(AppConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检测目录是否存在
|
// 检测目录是否存在
|
||||||
func checkDirExists(path ...string) {
|
func checkDirExists(path ...string) {
|
||||||
for _, value := range path {
|
for _, value := range path {
|
||||||
_, err := os.Stat(value)
|
_, err := os.Stat(value)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
panic(value + "目录不存在")
|
panic(value + "目录不存在")
|
||||||
}
|
}
|
||||||
if os.IsPermission(err) {
|
if os.IsPermission(err) {
|
||||||
panic(value + "目录无权限操作")
|
panic(value + "目录无权限操作")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package crontask
|
package crontask
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/robfig/cron"
|
"github.com/robfig/cron"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
var DefaultCronTask *CronTask
|
var DefaultCronTask *CronTask
|
||||||
|
@ -12,76 +12,76 @@ var DefaultCronTask *CronTask
|
||||||
type CronMap map[string]*cron.Cron
|
type CronMap map[string]*cron.Cron
|
||||||
|
|
||||||
type CronTask struct {
|
type CronTask struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
tasks CronMap
|
tasks CronMap
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCronTask() *CronTask {
|
func NewCronTask() *CronTask {
|
||||||
return &CronTask{
|
return &CronTask{
|
||||||
sync.RWMutex{},
|
sync.RWMutex{},
|
||||||
make(CronMap),
|
make(CronMap),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增定时任务,如果name存在,则添加失败
|
// 新增定时任务,如果name存在,则添加失败
|
||||||
// name 任务名称
|
// name 任务名称
|
||||||
// spec crontab时间格式定义 可定义多个时间\n分隔
|
// spec crontab时间格式定义 可定义多个时间\n分隔
|
||||||
func (cronTask *CronTask) Add(name string, spec string, cmd cron.FuncJob) (err error) {
|
func (cronTask *CronTask) Add(name string, spec string, cmd cron.FuncJob) (err error) {
|
||||||
if name == "" || spec == "" || cmd == nil {
|
if name == "" || spec == "" || cmd == nil {
|
||||||
return errors.New("参数不完整")
|
return errors.New("参数不完整")
|
||||||
}
|
}
|
||||||
if cronTask.IsExist(name) {
|
if cronTask.IsExist(name) {
|
||||||
return errors.New("任务已存在")
|
return errors.New("任务已存在")
|
||||||
}
|
}
|
||||||
|
|
||||||
spec = strings.TrimSpace(spec)
|
spec = strings.TrimSpace(spec)
|
||||||
cronTask.Lock()
|
cronTask.Lock()
|
||||||
defer cronTask.Unlock()
|
defer cronTask.Unlock()
|
||||||
cronTask.tasks[name] = cron.New()
|
cronTask.tasks[name] = cron.New()
|
||||||
specs := strings.Split(spec, "|||")
|
specs := strings.Split(spec, "|||")
|
||||||
for _, item := range specs {
|
for _, item := range specs {
|
||||||
_, err = cron.Parse(item)
|
_, err = cron.Parse(item)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, item := range specs {
|
for _, item := range specs {
|
||||||
err = cronTask.tasks[name].AddFunc(item, cmd)
|
err = cronTask.tasks[name].AddFunc(item, cmd)
|
||||||
}
|
}
|
||||||
cronTask.tasks[name].Start()
|
cronTask.tasks[name].Start()
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 任务不存在则新增,任务已存在则删除后新增
|
// 任务不存在则新增,任务已存在则删除后新增
|
||||||
func (cronTask *CronTask) AddOrReplace(name string, spec string, cmd cron.FuncJob) error {
|
func (cronTask *CronTask) AddOrReplace(name string, spec string, cmd cron.FuncJob) error {
|
||||||
if cronTask.IsExist(name) {
|
if cronTask.IsExist(name) {
|
||||||
cronTask.Delete(name)
|
cronTask.Delete(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
return cronTask.Add(name, spec, cmd)
|
return cronTask.Add(name, spec, cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断任务是否存在
|
// 判断任务是否存在
|
||||||
func (cronTask *CronTask) IsExist(name string) bool {
|
func (cronTask *CronTask) IsExist(name string) bool {
|
||||||
cronTask.RLock()
|
cronTask.RLock()
|
||||||
defer cronTask.RUnlock()
|
defer cronTask.RUnlock()
|
||||||
_, ok := cronTask.tasks[name]
|
_, ok := cronTask.tasks[name]
|
||||||
|
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// 停止任务
|
// 停止任务
|
||||||
func (cronTask *CronTask) Stop(name string) {
|
func (cronTask *CronTask) Stop(name string) {
|
||||||
if cronTask.IsExist(name) {
|
if cronTask.IsExist(name) {
|
||||||
cronTask.tasks[name].Stop()
|
cronTask.tasks[name].Stop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除任务
|
// 删除任务
|
||||||
func (cronTask *CronTask) Delete(name string) {
|
func (cronTask *CronTask) Delete(name string) {
|
||||||
cronTask.Stop(name)
|
cronTask.Stop(name)
|
||||||
cronTask.Lock()
|
cronTask.Lock()
|
||||||
defer cronTask.Unlock()
|
defer cronTask.Unlock()
|
||||||
delete(cronTask.tasks, name)
|
delete(cronTask.tasks, name)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,43 +1,43 @@
|
||||||
package setting
|
package setting
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"gopkg.in/ini.v1"
|
"gopkg.in/ini.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 读取配置
|
// 读取配置
|
||||||
func Read(filename string) (config *ini.File, err error) {
|
func Read(filename string) (config *ini.File, err error) {
|
||||||
config, err = ini.Load(filename)
|
config, err = ini.Load(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 写入配置
|
// 写入配置
|
||||||
func Write(config map[string]map[string]string, filename string) error {
|
func Write(config map[string]map[string]string, filename string) error {
|
||||||
if len(config) == 0 {
|
if len(config) == 0 {
|
||||||
return errors.New("参数不能为空")
|
return errors.New("参数不能为空")
|
||||||
}
|
}
|
||||||
|
|
||||||
file := ini.Empty()
|
file := ini.Empty()
|
||||||
for sectionName, items := range config {
|
for sectionName, items := range config {
|
||||||
if sectionName == "" {
|
if sectionName == "" {
|
||||||
return errors.New("节名称不能为空")
|
return errors.New("节名称不能为空")
|
||||||
}
|
}
|
||||||
section, err := file.NewSection(sectionName)
|
section, err := file.NewSection(sectionName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for key, value := range items {
|
for key, value := range items {
|
||||||
_, err = section.NewKey(key, value)
|
_, err = section.NewKey(key, value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err := file.SaveTo(filename)
|
err := file.SaveTo(filename)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,9 @@ import "encoding/json"
|
||||||
// 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 Json struct{}
|
type Json struct{}
|
||||||
|
@ -16,24 +16,24 @@ const ResponseSuccess = 0
|
||||||
const ResponseFailure = 1
|
const ResponseFailure = 1
|
||||||
|
|
||||||
func (j *Json) Success(message string, data interface{}) string {
|
func (j *Json) Success(message string, data interface{}) string {
|
||||||
return j.response(ResponseSuccess, message, data)
|
return j.response(ResponseSuccess, message, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *Json) Failure(code int, message string) string {
|
func (j *Json) Failure(code int, message string) string {
|
||||||
return j.response(code, message, nil)
|
return j.response(code, message, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *Json) response(code int, message string, data interface{}) string {
|
func (j *Json) 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 {
|
||||||
RecordLog(err)
|
RecordLog(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return string(result)
|
return string(result)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,52 +1,52 @@
|
||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"log"
|
"log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 执行shell命令
|
// 执行shell命令
|
||||||
func ExecShell(command string, args ...string) (string, error) {
|
func ExecShell(command string, args ...string) (string, error) {
|
||||||
result, err := exec.Command(command, args...).CombinedOutput()
|
result, err := exec.Command(command, args...).CombinedOutput()
|
||||||
|
|
||||||
return string(result), err
|
return string(result), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生成长度为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)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 日志记录
|
// 日志记录
|
||||||
// todo 保存到哪里 文件,数据库还是elasticsearch?,暂时输出到终端
|
// todo 保存到哪里 文件,数据库还是elasticsearch?,暂时输出到终端
|
||||||
func RecordLog(v ...interface{}) {
|
func RecordLog(v ...interface{}) {
|
||||||
log.Println(v)
|
log.Println(v)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,29 +3,29 @@ package utils
|
||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
func TestExecShell(t *testing.T) {
|
func TestExecShell(t *testing.T) {
|
||||||
_, err := ExecShell("ls")
|
_, err := ExecShell("ls")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,108 +1,108 @@
|
||||||
package install
|
package install
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/ouqiang/cron-scheduler/models"
|
"github.com/ouqiang/cron-scheduler/models"
|
||||||
"github.com/ouqiang/cron-scheduler/modules/app"
|
"github.com/ouqiang/cron-scheduler/modules/app"
|
||||||
"github.com/ouqiang/cron-scheduler/modules/setting"
|
"github.com/ouqiang/cron-scheduler/modules/setting"
|
||||||
"github.com/ouqiang/cron-scheduler/modules/utils"
|
"github.com/ouqiang/cron-scheduler/modules/utils"
|
||||||
"gopkg.in/macaron.v1"
|
"gopkg.in/macaron.v1"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 系统安装
|
// 系统安装
|
||||||
|
|
||||||
type InstallForm struct {
|
type InstallForm struct {
|
||||||
DbType string `binding:"IN(mysql)"`
|
DbType string `binding:"IN(mysql)"`
|
||||||
DbHost string `binding:"Required"`
|
DbHost string `binding:"Required"`
|
||||||
DbPort int `binding:"Required"`
|
DbPort int `binding:"Required"`
|
||||||
DbUsername string `binding:"Required"`
|
DbUsername string `binding:"Required"`
|
||||||
DbPassword string `binding:"Required"`
|
DbPassword string `binding:"Required"`
|
||||||
DbName string `binding:"Required"`
|
DbName string `binding:"Required"`
|
||||||
DbTablePrefix string
|
DbTablePrefix string
|
||||||
AdminUsername string `binding:"Required;MinSize(3)"`
|
AdminUsername string `binding:"Required;MinSize(3)"`
|
||||||
AdminPassword string `binding:"Required;MinSize(6)"`
|
AdminPassword string `binding:"Required;MinSize(6)"`
|
||||||
AdminEmail string `binding:"Email"`
|
AdminEmail string `binding:"Email"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示安装页面
|
// 显示安装页面
|
||||||
func Show(ctx *macaron.Context) {
|
func Show(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/show")
|
ctx.HTML(200, "install/show")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 安装,
|
// 安装,
|
||||||
func Install(ctx *macaron.Context, form InstallForm) string {
|
func Install(ctx *macaron.Context, form InstallForm) string {
|
||||||
json := utils.Json{}
|
json := utils.Json{}
|
||||||
if app.Installed {
|
if app.Installed {
|
||||||
return json.Failure(utils.ResponseFailure, "系统已安装成功")
|
return json.Failure(utils.ResponseFailure, "系统已安装成功")
|
||||||
}
|
}
|
||||||
// 写入数据库配置
|
// 写入数据库配置
|
||||||
err := writeConfig(form)
|
err := writeConfig(form)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.RecordLog(err)
|
utils.RecordLog(err)
|
||||||
return json.Failure(utils.ResponseFailure, "数据库配置写入文件失败")
|
return json.Failure(utils.ResponseFailure, "数据库配置写入文件失败")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化Db
|
// 初始化Db
|
||||||
app.InitDb()
|
app.InitDb()
|
||||||
// 创建数据库表
|
// 创建数据库表
|
||||||
migration := new(models.Migration)
|
migration := new(models.Migration)
|
||||||
err = migration.Exec(form.DbName)
|
err = migration.Exec(form.DbName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.RecordLog(err)
|
utils.RecordLog(err)
|
||||||
return json.Failure(utils.ResponseFailure, "创建数据库表失败")
|
return json.Failure(utils.ResponseFailure, "创建数据库表失败")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建管理员账号
|
// 创建管理员账号
|
||||||
err = createAdminUser(form)
|
err = createAdminUser(form)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.RecordLog(err)
|
utils.RecordLog(err)
|
||||||
return json.Failure(utils.ResponseFailure, "创建管理员账号失败")
|
return json.Failure(utils.ResponseFailure, "创建管理员账号失败")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建安装锁
|
// 创建安装锁
|
||||||
err = app.CreateInstallLock()
|
err = app.CreateInstallLock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.RecordLog(err)
|
utils.RecordLog(err)
|
||||||
return json.Failure(utils.ResponseFailure, "创建文件安装锁失败")
|
return json.Failure(utils.ResponseFailure, "创建文件安装锁失败")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化定时任务等
|
// 初始化定时任务等
|
||||||
app.InitResource()
|
app.InitResource()
|
||||||
|
|
||||||
return json.Success("安装成功", nil)
|
return json.Success("安装成功", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 数据库配置写入文件
|
// 数据库配置写入文件
|
||||||
func writeConfig(form InstallForm) error {
|
func writeConfig(form InstallForm) error {
|
||||||
dbConfig := map[string]map[string]string{
|
dbConfig := map[string]map[string]string{
|
||||||
"db": map[string]string{
|
"db": map[string]string{
|
||||||
"engine": form.DbType,
|
"engine": form.DbType,
|
||||||
"host": form.DbHost,
|
"host": form.DbHost,
|
||||||
"port": strconv.Itoa(form.DbPort),
|
"port": strconv.Itoa(form.DbPort),
|
||||||
"user": form.DbUsername,
|
"user": form.DbUsername,
|
||||||
"password": form.DbPassword,
|
"password": form.DbPassword,
|
||||||
"database": form.DbName,
|
"database": form.DbName,
|
||||||
"prefix": form.DbTablePrefix,
|
"prefix": form.DbTablePrefix,
|
||||||
"charset": "utf8",
|
"charset": "utf8",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,30 @@
|
||||||
package routers
|
package routers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/go-macaron/binding"
|
"github.com/go-macaron/binding"
|
||||||
"github.com/ouqiang/cron-scheduler/routers/install"
|
"github.com/ouqiang/cron-scheduler/routers/install"
|
||||||
"gopkg.in/macaron.v1"
|
"gopkg.in/macaron.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 路由注册
|
// 路由注册
|
||||||
func Register(m *macaron.Macaron) {
|
func Register(m *macaron.Macaron) {
|
||||||
// 所有GET方法,自动注册HEAD方法
|
// 所有GET方法,自动注册HEAD方法
|
||||||
m.SetAutoHead(true)
|
m.SetAutoHead(true)
|
||||||
// 404错误
|
// 404错误
|
||||||
m.NotFound(func(ctx *macaron.Context) {
|
m.NotFound(func(ctx *macaron.Context) {
|
||||||
ctx.HTML(404, "error/404")
|
ctx.HTML(404, "error/404")
|
||||||
})
|
})
|
||||||
// 50x错误
|
// 50x错误
|
||||||
m.InternalServerError(func(ctx *macaron.Context) {
|
m.InternalServerError(func(ctx *macaron.Context) {
|
||||||
ctx.HTML(500, "error/500")
|
ctx.HTML(500, "error/500")
|
||||||
})
|
})
|
||||||
// 首页
|
// 首页
|
||||||
m.Get("/", func(ctx *macaron.Context) string {
|
m.Get("/", func(ctx *macaron.Context) string {
|
||||||
return "go home"
|
return "go home"
|
||||||
})
|
})
|
||||||
// 系统安装
|
// 系统安装
|
||||||
m.Group("/install", func() {
|
m.Group("/install", func() {
|
||||||
m.Get("", install.Show)
|
m.Get("", install.Show)
|
||||||
m.Post("", binding.Bind(install.InstallForm{}), install.Install)
|
m.Post("", binding.Bind(install.InstallForm{}), install.Install)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
242
service/task.go
242
service/task.go
|
@ -1,175 +1,175 @@
|
||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/ouqiang/cron-scheduler/models"
|
"github.com/ouqiang/cron-scheduler/models"
|
||||||
"github.com/ouqiang/cron-scheduler/modules/ansible"
|
"github.com/ouqiang/cron-scheduler/modules/ansible"
|
||||||
"github.com/ouqiang/cron-scheduler/modules/crontask"
|
"github.com/ouqiang/cron-scheduler/modules/crontask"
|
||||||
"github.com/ouqiang/cron-scheduler/modules/utils"
|
"github.com/ouqiang/cron-scheduler/modules/utils"
|
||||||
"github.com/robfig/cron"
|
"github.com/robfig/cron"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Task struct{}
|
type Task struct{}
|
||||||
|
|
||||||
// 初始化任务, 从数据库取出所有任务, 添加到定时任务并运行
|
// 初始化任务, 从数据库取出所有任务, 添加到定时任务并运行
|
||||||
func (task *Task) Initialize() {
|
func (task *Task) Initialize() {
|
||||||
taskModel := new(models.Task)
|
taskModel := new(models.Task)
|
||||||
taskList, err := taskModel.ActiveList()
|
taskList, err := taskModel.ActiveList()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.RecordLog("获取任务列表错误-", err.Error())
|
utils.RecordLog("获取任务列表错误-", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(taskList) == 0 {
|
if len(taskList) == 0 {
|
||||||
utils.RecordLog("任务列表为空")
|
utils.RecordLog("任务列表为空")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, item := range taskList {
|
for _, item := range taskList {
|
||||||
task.Add(item)
|
task.Add(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加任务
|
// 添加任务
|
||||||
func (task *Task) Add(taskModel models.Task) {
|
func (task *Task) Add(taskModel models.Task) {
|
||||||
taskFunc := createHandlerJob(taskModel)
|
taskFunc := createHandlerJob(taskModel)
|
||||||
if taskFunc == nil {
|
if taskFunc == nil {
|
||||||
utils.RecordLog("添加任务#不存在的任务协议编号", taskModel.Protocol)
|
utils.RecordLog("添加任务#不存在的任务协议编号", taskModel.Protocol)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 定时任务
|
// 定时任务
|
||||||
if taskModel.Type == models.Timing {
|
if taskModel.Type == models.Timing {
|
||||||
err := crontask.DefaultCronTask.AddOrReplace(strconv.Itoa(taskModel.Id), taskModel.Spec, taskFunc)
|
err := crontask.DefaultCronTask.AddOrReplace(strconv.Itoa(taskModel.Id), taskModel.Spec, taskFunc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.RecordLog(err)
|
utils.RecordLog(err)
|
||||||
}
|
}
|
||||||
} else if taskModel.Type == models.Delay {
|
} else if taskModel.Type == models.Delay {
|
||||||
// 延时任务
|
// 延时任务
|
||||||
time.AfterFunc(time.Duration(taskModel.Delay)*time.Second, taskFunc)
|
time.AfterFunc(time.Duration(taskModel.Delay)*time.Second, taskFunc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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{}
|
||||||
|
|
||||||
func (h *HTTPHandler) Run(taskModel models.Task) (result string, err error) {
|
func (h *HTTPHandler) Run(taskModel models.Task) (result string, err error) {
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
if taskModel.Timeout > 0 {
|
if taskModel.Timeout > 0 {
|
||||||
client.Timeout = time.Duration(taskModel.Timeout) * time.Second
|
client.Timeout = time.Duration(taskModel.Timeout) * time.Second
|
||||||
}
|
}
|
||||||
req, err := http.NewRequest("POST", taskModel.Command, nil)
|
req, err := http.NewRequest("POST", taskModel.Command, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.RecordLog("创建HTTP请求错误-", err.Error())
|
utils.RecordLog("创建HTTP请求错误-", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
req.Header.Set("Content-type", "application/x-www-form-urlencoded")
|
req.Header.Set("Content-type", "application/x-www-form-urlencoded")
|
||||||
req.Header.Set("User-Agent", "golang-cron/scheduler")
|
req.Header.Set("User-Agent", "golang-cron/scheduler")
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
defer func() {
|
defer func() {
|
||||||
if resp != nil {
|
if resp != nil {
|
||||||
resp.Body.Close()
|
resp.Body.Close()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.RecordLog("HTTP请求错误-", err.Error())
|
utils.RecordLog("HTTP请求错误-", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.RecordLog("读取HTTP请求返回值失败-", err.Error())
|
utils.RecordLog("读取HTTP请求返回值失败-", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return string(body), err
|
return string(body), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// SSH-command任务
|
// SSH-command任务
|
||||||
type SSHCommandHandler struct{}
|
type SSHCommandHandler struct{}
|
||||||
|
|
||||||
func (ssh *SSHCommandHandler) Run(taskModel models.Task) (string, error) {
|
func (ssh *SSHCommandHandler) Run(taskModel models.Task) (string, error) {
|
||||||
return execSSHHandler("shell", taskModel)
|
return execSSHHandler("shell", taskModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SSH-script任务
|
// SSH-script任务
|
||||||
type SSHScriptHandler struct{}
|
type SSHScriptHandler struct{}
|
||||||
|
|
||||||
func (ssh *SSHScriptHandler) Run(taskModel models.Task) (string, error) {
|
func (ssh *SSHScriptHandler) Run(taskModel models.Task) (string, error) {
|
||||||
return execSSHHandler("script", taskModel)
|
return execSSHHandler("script", taskModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SSH任务
|
// SSH任务
|
||||||
func execSSHHandler(module string, taskModel models.Task) (string, error) {
|
func execSSHHandler(module string, taskModel models.Task) (string, error) {
|
||||||
var args []string = []string{taskModel.Command}
|
var args []string = []string{taskModel.Command}
|
||||||
if taskModel.Timeout > 0 {
|
if taskModel.Timeout > 0 {
|
||||||
// -B 异步执行超时时间, -P 轮询时间
|
// -B 异步执行超时时间, -P 轮询时间
|
||||||
args = append(args, "-B", strconv.Itoa(taskModel.Timeout), "-P", "10")
|
args = append(args, "-B", strconv.Itoa(taskModel.Timeout), "-P", "10")
|
||||||
}
|
}
|
||||||
if module == "shell" {
|
if module == "shell" {
|
||||||
return ansible.Shell(taskModel.SshHosts, ansible.DefaultHosts.GetFilename(), args...)
|
return ansible.Shell(taskModel.SshHosts, ansible.DefaultHosts.GetFilename(), args...)
|
||||||
}
|
}
|
||||||
if module == "script" {
|
if module == "script" {
|
||||||
return ansible.Script(taskModel.SshHosts, ansible.DefaultHosts.GetFilename(), args...)
|
return ansible.Script(taskModel.SshHosts, ansible.DefaultHosts.GetFilename(), args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createTaskLog(taskId int) (int, error) {
|
func createTaskLog(taskId int) (int, error) {
|
||||||
taskLogModel := new(models.TaskLog)
|
taskLogModel := new(models.TaskLog)
|
||||||
taskLogModel.TaskId = taskId
|
taskLogModel.TaskId = taskId
|
||||||
taskLogModel.StartTime = time.Now()
|
taskLogModel.StartTime = time.Now()
|
||||||
taskLogModel.Status = models.Running
|
taskLogModel.Status = models.Running
|
||||||
insertId, err := taskLogModel.Create()
|
insertId, err := taskLogModel.Create()
|
||||||
|
|
||||||
return insertId, err
|
return insertId, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateTaskLog(taskLogId int, result string, err error) (int64, error) {
|
func updateTaskLog(taskLogId int, result string, err error) (int64, error) {
|
||||||
fmt.Println(taskLogId)
|
fmt.Println(taskLogId)
|
||||||
taskLogModel := new(models.TaskLog)
|
taskLogModel := new(models.TaskLog)
|
||||||
var status models.Status
|
var status models.Status
|
||||||
if err != nil {
|
if err != nil {
|
||||||
result = err.Error() + " " + result
|
result = err.Error() + " " + result
|
||||||
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{
|
||||||
"status": status,
|
"status": status,
|
||||||
"result": result,
|
"result": result,
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func createHandlerJob(taskModel models.Task) cron.FuncJob {
|
func createHandlerJob(taskModel models.Task) cron.FuncJob {
|
||||||
var handler Handler = nil
|
var handler Handler = nil
|
||||||
switch taskModel.Protocol {
|
switch taskModel.Protocol {
|
||||||
case models.HTTP:
|
case models.HTTP:
|
||||||
handler = new(HTTPHandler)
|
handler = new(HTTPHandler)
|
||||||
case models.SSHCommand:
|
case models.SSHCommand:
|
||||||
handler = new(SSHCommandHandler)
|
handler = new(SSHCommandHandler)
|
||||||
case models.SSHScript:
|
case models.SSHScript:
|
||||||
handler = new(SSHScriptHandler)
|
handler = new(SSHScriptHandler)
|
||||||
}
|
}
|
||||||
taskFunc := func() {
|
taskFunc := func() {
|
||||||
taskLogId, err := createTaskLog(taskModel.Id)
|
taskLogId, err := createTaskLog(taskModel.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.RecordLog("写入任务日志失败-", err)
|
utils.RecordLog("写入任务日志失败-", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// err != nil 执行失败
|
// err != nil 执行失败
|
||||||
result, err := handler.Run(taskModel)
|
result, err := handler.Run(taskModel)
|
||||||
_, err = updateTaskLog(int(taskLogId), result, err)
|
_, err = updateTaskLog(int(taskLogId), result, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.RecordLog("更新任务日志失败-", err)
|
utils.RecordLog("更新任务日志失败-", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return taskFunc
|
return taskFunc
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue