完善HTTP任务、SSH任务

pull/21/merge
ouqiang 2017-03-24 17:55:44 +08:00
parent c556e40912
commit 1533dc3ca1
12 changed files with 128 additions and 135 deletions

View File

@ -7,8 +7,6 @@ import (
"github.com/go-macaron/session" "github.com/go-macaron/session"
"github.com/go-macaron/csrf" "github.com/go-macaron/csrf"
"github.com/ouqiang/cron-scheduler/modules/app" "github.com/ouqiang/cron-scheduler/modules/app"
"fmt"
"os"
"github.com/ouqiang/cron-scheduler/routers" "github.com/ouqiang/cron-scheduler/routers"
) )
@ -31,10 +29,7 @@ var CmdWeb = cli.Command{
} }
func run(ctx *cli.Context) { func run(ctx *cli.Context) {
// 检测环境 app.InitEnv()
app.CheckEnv()
// 启动定时任务
runScheduler()
m := macaron.Classic() m := macaron.Classic()
// 注册路由 // 注册路由
routers.Register(m) routers.Register(m)
@ -44,12 +39,6 @@ func run(ctx *cli.Context) {
m.Run(port) m.Run(port)
} }
// 定时任务调度
func runScheduler() {
fmt.Println("hello world")
os.Exit(1)
}
// 中间件注册 // 中间件注册
func registerMiddleware(m *macaron.Macaron) { func registerMiddleware(m *macaron.Macaron) {
m.Use(macaron.Logger()) m.Use(macaron.Logger())

View File

@ -3,7 +3,7 @@ package main
/*-------------------------------------------------------- /*--------------------------------------------------------
Linux crontab Linux crontab
HTTPSSH HTTPSSH
--------------------------------------------------------*/ --------------------------------------------------------*/
import ( import (

View File

@ -23,8 +23,13 @@ const (
) )
// 新增 // 新增
func(host *Host) Create() (int64, error) { func(host *Host) Create() (insertId int16, err error) {
return Db.Insert(host) _, err = Db.Insert(host)
if err == nil {
insertId = host.Id
}
return
} }
// 更新 // 更新

View File

@ -26,7 +26,8 @@ type Task struct {
Protocol Protocol `xorm:"tinyint notnull"` // 协议 1:http 2:ssh Protocol Protocol `xorm:"tinyint notnull"` // 协议 1:http 2:ssh
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秒后执行 Timeout int `xorm:"mediumint notnull default 0"` // 任务执行超时时间(单位秒),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"` // 创建时间
@ -37,10 +38,15 @@ type Task struct {
} }
// 新增 // 新增
func(task *Task) Create() (int64, error) { func(task *Task) Create() (insertId int, err error) {
task.Status = Enabled task.Status = Enabled
return Db.Insert(task) _, err = Db.Insert(task)
if err == nil {
insertId = task.Id
}
return
} }
// 更新 // 更新
@ -63,6 +69,14 @@ 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) {
task.parsePageAndPageSize()
list := make([]Task, 0)
err := Db.Where("status = ?", Enabled).Desc("id").Find(&list)
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)

View File

@ -16,10 +16,15 @@ type TaskLog struct{
PageSize int `xorm:"-"` PageSize int `xorm:"-"`
} }
func(taskLog *TaskLog) Create() (int64, error) { func(taskLog *TaskLog) Create() (insertId int, err error) {
taskLog.Status = Running taskLog.Status = Running
return Db.Insert(taskLog) _, err = Db.Insert(taskLog)
if err == nil {
insertId = taskLog.Id
}
return
} }
// 更新 // 更新

View File

@ -24,12 +24,17 @@ type User struct {
} }
// 新增 // 新增
func(user *User) Create() (int64, 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)
return Db.Insert(user) _, err = Db.Insert(user)
if err == nil {
insertId = user.Id
}
return
} }
// 更新 // 更新

View File

@ -1,97 +1,29 @@
package ansible package ansible
// ansible ad-hoc playbook命令封装 // ansible ad-hoc 命令封装
import ( import (
"os"
"errors" "errors"
"gopkg.in/yaml.v2"
"io/ioutil"
"github.com/ouqiang/cron-scheduler/modules/utils" "github.com/ouqiang/cron-scheduler/modules/utils"
) )
type Handler map[string]interface{}
type Playbook struct {
Name string
Hosts string
Tasks []Handler
Handlers []Handler
}
func(playbook *Playbook) SetHosts(hosts string) {
playbook.Hosts = hosts
}
func(playbook *Playbook) SetName(name string) {
playbook.Name = name
}
func(playbook *Playbook) AddTask(handler Handler) {
playbook.Tasks = append(playbook.Tasks, handler)
}
func(playbook *Playbook) AddHandler(handler Handler) {
playbook.Handlers = append(playbook.Handlers, handler)
}
/** /**
* ad-hoc * ad-hoc
* hosts * hosts
* hostFile
* module * module
* args * args
*/ */
func ExecCommand(hosts string, module string, args... string) (output string, err error) { func ExecCommand(hosts string, hostFile string, args... string) (output string, err error) {
if hosts== "" || module == "" { if hosts== "" || hostFile == "" || len(args) == 0 {
err = errors.New("参数不完整") err = errors.New("参数不完整")
return return
} }
hostFile := DefaultHosts.GetFilename() commandArgs := []string{hosts, "-i", hostFile}
commandArgs := []string{hosts, "-i", hostFile, "-m", module} commandArgs = append(commandArgs, args...)
if len(args) != 0 {
commandArgs = append(commandArgs, "-a")
commandArgs = append(commandArgs, args...)
}
output, err = utils.ExecShell("ansible", commandArgs...) output, err = utils.ExecShell("ansible", commandArgs...)
return return
} }
// 执行playbook
func ExecPlaybook(playbook Playbook) (result string, err error) {
data, err := yaml.Marshal([]Playbook{playbook})
if err != nil {
return
}
playbookFile, err := ioutil.TempFile(GetTmpDir(), "playbook")
if err != nil {
return
}
hostFile := DefaultHosts.GetFilename()
defer func() {
playbookFile.Close()
os.Remove(playbookFile.Name())
}()
_, err = playbookFile.Write(data)
if err != nil {
return
}
commandArgs := []string{"-i", hostFile, playbookFile.Name()}
result, err = utils.ExecShell("ansible-playbook", commandArgs...)
return
}
// 判断 获取临时目录,默认/dev/shm
func GetTmpDir() string {
dir := "/dev/shm"
_, err := os.Stat(dir)
if os.IsPermission(err) {
return ""
}
return dir
}

View File

@ -19,6 +19,7 @@ type Hosts struct {
func NewHosts(hostFilename string) *Hosts { func NewHosts(hostFilename string) *Hosts {
h := &Hosts{sync.RWMutex{}, hostFilename} h := &Hosts{sync.RWMutex{}, hostFilename}
h.Write()
return h return h
} }

View File

@ -8,6 +8,7 @@ import (
"github.com/ouqiang/cron-scheduler/modules/crontask" "github.com/ouqiang/cron-scheduler/modules/crontask"
"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/service"
) )
var ( var (
@ -20,7 +21,8 @@ var (
Installed bool // 应用是否安装过 Installed bool // 应用是否安装过
) )
func init() { func InitEnv() {
CheckEnv()
wd, err := os.Getwd() wd, err := os.Getwd()
if err != nil { if err != nil {
panic(err) panic(err)
@ -79,14 +81,14 @@ func CreateInstallLock() error {
// 初始化资源 // 初始化资源
func InitResource() { func InitResource() {
// 初始化定时任务
crontask.DefaultCronTask = crontask.NewCronTask()
// 初始化DB // 初始化DB
models.Db = models.CreateDb(AppConfig) models.Db = models.CreateDb(AppConfig)
// 初始化ansible Hosts // 初始化ansible Hosts
ansible.DefaultHosts = ansible.NewHosts(AnsibleHosts) ansible.DefaultHosts = ansible.NewHosts(AnsibleHosts)
ansible.DefaultHosts.Write() // 初始化定时任务
os.Exit(1) crontask.DefaultCronTask = crontask.NewCronTask()
serviceTask := new(service.Task)
serviceTask.Initialize()
} }
// 检测目录是否存在 // 检测目录是否存在

View File

@ -34,10 +34,11 @@ func(cronTask *CronTask) Add(name string, spec string, cmd cron.FuncJob ) (err e
return errors.New("任务已存在") return errors.New("任务已存在")
} }
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, "\n") 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 {
@ -93,7 +94,7 @@ func(cronTask *CronTask) Delete(name string) {
} }
// 运行所有任务 // 运行所有任务
func(cronTask *CronTask) run() { func(cronTask *CronTask) Run() {
for _, cron := range cronTask.tasks { for _, cron := range cronTask.tasks {
// cron内部有开启goroutine,此处不用新建goroutine // cron内部有开启goroutine,此处不用新建goroutine
cron.Start() cron.Start()

View File

@ -1,13 +1,15 @@
package routers package routers
import "gopkg.in/macaron.v1" import (
"gopkg.in/macaron.v1"
)
// 路由注册 // 路由注册
func Register(m *macaron.Macaron) { func Register(m *macaron.Macaron) {
// 所有GET方法自动注册HEAD方法 // 所有GET方法自动注册HEAD方法
m.SetAutoHead(true) m.SetAutoHead(true)
// 首页 // 首页
m.Get("/", func(ctx *macaron.Context) (string) { m.Any("/", func(ctx *macaron.Context) (string) {
return "go home" return "go home"
}) })
} }

View File

@ -9,15 +9,16 @@ import (
"time" "time"
"github.com/ouqiang/cron-scheduler/modules/crontask" "github.com/ouqiang/cron-scheduler/modules/crontask"
"github.com/robfig/cron" "github.com/robfig/cron"
"errors" "github.com/ouqiang/cron-scheduler/modules/ansible"
"fmt"
) )
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.List() taskList, err := taskModel.ActiveList()
if err != nil { if err != nil {
utils.RecordLog("获取任务列表错误-", err.Error()) utils.RecordLog("获取任务列表错误-", err.Error())
return return
@ -29,8 +30,11 @@ func(task *Task) Initialize() {
for _, item := range(taskList) { for _, item := range(taskList) {
task.Add(item) task.Add(item)
} }
crontask.DefaultCronTask.Run()
} }
// 添加任务 // 添加任务
func(task *Task) Add(taskModel models.Task) { func(task *Task) Add(taskModel models.Task) {
taskFunc := createHandlerJob(taskModel) taskFunc := createHandlerJob(taskModel)
@ -40,21 +44,24 @@ func(task *Task) Add(taskModel models.Task) {
} }
// 定时任务 // 定时任务
if taskModel.Type == models.Timing { if taskModel.Type == models.Timing {
crontask.DefaultCronTask.AddOrReplace(strconv.Itoa(taskModel.Id), taskModel.Spec, taskFunc) err := crontask.DefaultCronTask.AddOrReplace(strconv.Itoa(taskModel.Id), taskModel.Spec, taskFunc)
if err != nil {
utils.RecordLog(err)
}
} else if taskModel.Type == models.Delay { } else if taskModel.Type == models.Delay {
// 延时任务 // 延时任务
time.AfterFunc(time.Duration(taskModel.Timeout), taskFunc) time.AfterFunc(time.Duration(taskModel.Delay) * time.Second, taskFunc)
} }
} }
type Handler interface { type Handler interface {
Run(taskModel models.Task) Run(taskModel models.Task) (string, error)
} }
// HTTP任务 // HTTP任务
type HTTPHandler struct {} type HTTPHandler struct {}
func(h *HTTPHandler) Run(taskModel models.Task) { 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
@ -68,7 +75,11 @@ func(h *HTTPHandler) Run(taskModel models.Task) {
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 resp.Body.Close() defer func() {
if resp != nil {
resp.Body.Close()
}
}()
if err != nil { if err != nil {
utils.RecordLog("HTTP请求错误-", err.Error()) utils.RecordLog("HTTP请求错误-", err.Error())
return return
@ -78,18 +89,30 @@ func(h *HTTPHandler) Run(taskModel models.Task) {
utils.RecordLog("读取HTTP请求返回值失败-", err.Error()) utils.RecordLog("读取HTTP请求返回值失败-", err.Error())
} }
return string(body),err
} }
// SSH任务 // SSH任务
type SSHHandler struct {} type SSHHandler struct {}
func(ssh *SSHHandler) Run(taskModel models.Task) { func(ssh *SSHHandler) Run(taskModel models.Task) (string, error) {
var args []string = []string{
"-m", "shell",
"-a", taskModel.Command,
}
if (taskModel.Timeout > 0) {
// -B 异步执行超时时间, -P 轮询时间
args = append(args, "-B", strconv.Itoa(taskModel.Timeout), "-P", "10")
}
result, err := ansible.ExecCommand(taskModel.SshHosts, ansible.DefaultHosts.GetFilename(), args...)
return result, err
} }
func createTaskLog(taskModel models.Task) (int64, error) { func createTaskLog(taskId int) (int, error) {
taskLogModel := new(models.TaskLog) taskLogModel := new(models.TaskLog)
taskLogModel.TaskId = taskModel.Id 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()
@ -97,30 +120,44 @@ func createTaskLog(taskModel models.Task) (int64, error) {
return insertId, err return insertId, err
} }
func updateTaskLog(taskModel models.Task, result string) { func updateTaskLog(taskLogId int, result string, err error) (int64, error) {
fmt.Println(taskLogId)
taskLogModel := new(models.TaskLog) taskLogModel := new(models.TaskLog)
taskLogModel.TaskId= taskModel.Id var status models.Status
taskLogModel.StartTime = time.Now() if err != nil {
result = err.Error() + " " + result
status = models.Failure
} else {
status = models.Finish
}
return taskLogModel.Update(taskLogId, models.CommonMap{
"status": status,
"result": result,
});
} }
func createHandlerJob(taskModel models.Task) cron.FuncJob { func createHandlerJob(taskModel models.Task) cron.FuncJob {
var taskFunc cron.FuncJob = nil; var handler Handler = nil
switch taskModel.Protocol { switch taskModel.Protocol {
case models.HTTP: case models.HTTP:
taskFunc = func() { handler = new(HTTPHandler)
var handler Handler = new(HTTPHandler)
createTaskLog(taskModel)
handler.Run(taskModel)
}
case models.SSH: case models.SSH:
taskFunc = func() { handler = new(SSHHandler)
var handler Handler = new(SSHHandler) }
createTaskLog(taskModel) taskFunc := func() {
handler.Run(taskModel) taskLogId, err := createTaskLog(taskModel.Id)
} if err != nil {
utils.RecordLog("写入任务日志失败-", err)
return
}
// err != nil 执行失败
result, err := handler.Run(taskModel)
_, err = updateTaskLog(int(taskLogId), result, err)
if err != nil {
utils.RecordLog("更新任务日志失败-", err)
}
} }
return taskFunc return taskFunc
} }