增加本地命令执行

pull/21/merge
ouqiang 2017-04-17 02:01:41 +08:00
parent dcf86e00a3
commit f99db815de
7 changed files with 48 additions and 39 deletions

View File

@ -9,6 +9,7 @@ type TaskProtocol int8
const (
TaskHTTP TaskProtocol = iota + 1 // HTTP协议
TaskSSH // SSH命令
TaskLocalCommand // 本地命令
)
// 任务
@ -16,7 +17,7 @@ type Task struct {
Id int `xorm:"int pk autoincr"`
Name string `xorm:"varchar(64) notnull"` // 任务名称
Spec string `xorm:"varchar(64) notnull"` // crontab
Protocol TaskProtocol `xorm:"tinyint notnull"` // 协议 1:http 2:ssh-command
Protocol TaskProtocol `xorm:"tinyint notnull"` // 协议 1:http 2:ssh-command 3: 本地命令
Command string `xorm:"varchar(512) notnull"` // URL地址或shell命令
Timeout int `xorm:"mediumint notnull default 0"` // 任务执行超时时间(单位秒),0不限制
HostId int16 `xorm:"smallint notnull default 0"` // SSH host id

View File

@ -25,6 +25,7 @@ func init() {
}
func InitEnv() {
runtime.GOMAXPROCS(runtime.NumCPU())
logger.InitLogger()
wd, err := os.Getwd()
if err != nil {

View File

@ -23,9 +23,26 @@ type Result struct {
// 执行shell命令
func Exec(sshConfig SSHConfig, cmd string) (output string, err error) {
client, err := getClient(sshConfig)
if err != nil {
return "", err
}
defer client.Close()
session, err := client.NewSession()
if err != nil {
return "", err
}
defer session.Close()
var resultChan chan Result = make(chan Result)
var timeoutChan chan bool = make(chan bool)
go execCommand(sshConfig, cmd, resultChan)
go func() {
cmd += fmt.Sprintf(" & { sleep %d; eval 'kill $!' &> /dev/null; }", sshConfig.ExecTimeout)
output, err := session.CombinedOutput(cmd)
resultChan <- Result{string(output), err}
}()
go triggerTimeout(timeoutChan, sshConfig.ExecTimeout)
select {
case result := <- resultChan:
@ -55,24 +72,6 @@ func getClient(sshConfig SSHConfig) (*ssh.Client, error) {
return ssh.Dial("tcp", addr, config)
}
func execCommand(sshConfig SSHConfig, cmd string, result chan Result) {
client, err := getClient(sshConfig)
if err != nil {
result <- Result{"", err}
return
}
defer client.Close()
session, err := client.NewSession()
if err != nil {
result <- Result{"", err}
return
}
defer session.Close()
output, err := session.CombinedOutput(cmd)
result <- Result{string(output), err}
}
func triggerTimeout(ch chan bool, timeout int){
// 最长执行时间不能超过24小时

View File

@ -37,7 +37,7 @@ func Create(ctx *macaron.Context) {
type TaskForm struct {
Name string `binding:"Required;"`
Spec string `binding:"Required;MaxSize(64)"`
Protocol models.TaskProtocol `binding:"In(1,2)"`
Protocol models.TaskProtocol `binding:"In(1,2,3)"`
Command string `binding:"Required;MaxSize(512)"`
Timeout int `binding:"Range(0,86400)"`
HostId int16

View File

@ -9,6 +9,8 @@ import (
"github.com/ouqiang/gocron/modules/logger"
"github.com/ouqiang/gocron/modules/ssh"
"github.com/jakecoffman/cron"
"github.com/ouqiang/gocron/modules/utils"
"strings"
)
var Cron *cron.Cron
@ -54,6 +56,19 @@ type Handler interface {
Run(taskModel models.TaskHost) (string, error)
}
type LocalCommandHandler struct {}
func (h *LocalCommandHandler) Run(taskModel models.TaskHost) (string, error) {
args := strings.Split(taskModel.Command, " ")
if len(args) > 1 {
return utils.ExecShell(args[0], args[1:]...)
}
return utils.ExecShell(args[0])
}
// HTTP任务
type HTTPHandler struct{}
@ -138,10 +153,12 @@ func updateTaskLog(taskLogId int64, result string, err error) (int64, error) {
func createHandlerJob(taskModel models.TaskHost) cron.FuncJob {
var handler Handler = nil
switch taskModel.Protocol {
case models.TaskHTTP:
handler = new(HTTPHandler)
case models.TaskSSH:
handler = new(SSHCommandHandler)
case models.TaskHTTP:
handler = new(HTTPHandler)
case models.TaskSSH:
handler = new(SSHCommandHandler)
case models.TaskLocalCommand:
handler = new(LocalCommandHandler)
}
if handler == nil {
return nil
@ -152,17 +169,7 @@ func createHandlerJob(taskModel models.TaskHost) cron.FuncJob {
logger.Error("任务开始执行#写入任务日志失败-", err)
return
}
// err != nil 执行失败, 失败重试3次
retryTime := 4
var result string = ""
for i := 0; i < retryTime; i++ {
result, err = handler.Run(taskModel)
if err == nil {
break
} else {
logger.Error("任务执行失败#tasklog.id-" + strconv.FormatInt(taskLogId, 10) + "#尝试次数-" + strconv.Itoa(i + 1) + "#" + err.Error() + " " + result)
}
}
result, err := handler.Run(taskModel)
_, err = updateTaskLog(taskLogId, result, err)
if err != nil {
logger.Error("任务结束#更新任务日志失败-", err)

View File

@ -42,7 +42,7 @@
<tr>
<td>{{{.Name}}}</td>
<td>{{{.Spec}}}</td>
<td>{{{if eq .Protocol 1}}} HTTP {{{else}}} SSH {{{end}}}</td>
<td>{{{if eq .Protocol 1}}} HTTP {{{else if eq .Protocol 2}}} SSH {{{else}}} 本地命令 {{{end}}}</td>
<td>{{{.Timeout}}}</td>
<td>{{{.Hostname}}}</td>
<td>

View File

@ -32,10 +32,11 @@
<div class="field">
<label>协议</label>
<div class="ui dropdown selection">
<input type="hidden" name="protocol" value="2">
<div class="default text">SSH-Command (执行shell命令)</div>
<input type="hidden" name="protocol" value="3">
<div class="default text">本地命令</div>
<i class="dropdown icon"></i>
<div class="menu">
<div class="item active" data-value="3">本地命令</div>
<div class="item active" data-value="2">SSH</div>
<div class="item" data-value="1">HTTP</div>
</div>