mirror of https://github.com/shunfei/cronsun
miraclesu
4 years ago
committed by
GitHub
4 changed files with 211 additions and 1 deletions
@ -0,0 +1,130 @@
|
||||
package cmd |
||||
|
||||
import ( |
||||
"bytes" |
||||
"encoding/json" |
||||
"fmt" |
||||
"math/rand" |
||||
"os/exec" |
||||
"strings" |
||||
"time" |
||||
|
||||
"github.com/shunfei/cronsun" |
||||
cron2 "github.com/shunfei/cronsun/node/cron" |
||||
"github.com/spf13/cobra" |
||||
) |
||||
|
||||
type cron struct { |
||||
timer string |
||||
cmd string |
||||
} |
||||
|
||||
var ( |
||||
importNodes string |
||||
) |
||||
|
||||
func init() { |
||||
ImportCmd.Flags().StringVar(&importNodes, "nodes", "", `the node ids that needs to run these imported job,
|
||||
split by ',', e.g: '--nodes=aa,bb,cc', empty means no node will run`) |
||||
} |
||||
|
||||
var ImportCmd = &cobra.Command{ |
||||
Use: "import", |
||||
Short: `it will load the job from the crontab, but you must to confirm you can execute 'crontab -l'`, |
||||
Run: func(cmd *cobra.Command, args []string) { |
||||
var nodeInclude []string |
||||
if len(importNodes) > 0 { |
||||
nodeInclude = strings.Split(importNodes, spliter) |
||||
} |
||||
|
||||
ea := NewExitAction() |
||||
crons, err := loadCrons() |
||||
if err != nil { |
||||
ea.Exit("load crontab failed,err:%s", err.Error()) |
||||
} |
||||
total := len(crons) |
||||
var successCount int |
||||
ea.After = func() { |
||||
fmt.Printf("total:%d,success:%d,failed:%d\n", total, successCount, total-successCount) |
||||
if err := cmd.Help(); err != nil { |
||||
return |
||||
} |
||||
} |
||||
rand.Seed(time.Now().Unix()) |
||||
for _, cron := range crons { |
||||
job := cronsun.Job{} |
||||
job.ID = cronsun.NextID() |
||||
job.Command = cron.cmd |
||||
jr := &cronsun.JobRule{ |
||||
Timer: "* " + cron.timer, |
||||
} |
||||
jr.NodeIDs = nodeInclude |
||||
job.Name = fmt.Sprintf("crontab-%d", rand.Intn(1000)) |
||||
job.Group = "crontab" |
||||
job.Rules = append(job.Rules, jr) |
||||
// 默认先暂停
|
||||
job.Pause = true |
||||
if err := job.Check(); err != nil { |
||||
ea.Exit("job check error:%s", err.Error()) |
||||
} |
||||
b, err := json.Marshal(job) |
||||
if err != nil { |
||||
ea.Exit("json marshal error:%s", err.Error()) |
||||
} |
||||
|
||||
_, err = cronsun.DefalutClient.Put(job.Key(), string(b)) |
||||
if err != nil { |
||||
ea.Exit("etcd put error:%s", err.Error()) |
||||
} |
||||
successCount++ |
||||
fmt.Printf("crontab-%s %s has import to the cronsun, the job id is:%s\n", cron.timer, cron.cmd, job.ID) |
||||
} |
||||
|
||||
fmt.Printf("import fininsh,succes:%d\n", successCount) |
||||
}, |
||||
} |
||||
|
||||
func checkCrons(crons []string) (invalid []string) { |
||||
for _, item := range crons { |
||||
item = strings.TrimSpace(item) |
||||
if item != "" && !strings.HasPrefix(item, "#") { |
||||
expr := strings.Fields(item) |
||||
expr = expr[:5] |
||||
_, err := cron2.ParseStandard(strings.Join(expr, " ")) |
||||
if err != nil { |
||||
invalid = append(invalid, item) |
||||
} |
||||
} |
||||
} |
||||
return |
||||
} |
||||
|
||||
func loadCrons() (crons []cron, err error) { |
||||
var b bytes.Buffer |
||||
cmd := exec.Command("crontab", "-l") |
||||
cmd.Stdout = &b |
||||
cmd.Stderr = &b |
||||
err = cmd.Run() |
||||
if err != nil { |
||||
return |
||||
} |
||||
|
||||
result := strings.Split(b.String(), "\n") |
||||
invalid := checkCrons(result) |
||||
if len(invalid) > 0 { |
||||
title := fmt.Sprintf("There are %d invalid cron expression,please check them at first.\n", len(invalid)) |
||||
err = fmt.Errorf(title + strings.Join(invalid, "\n")) |
||||
return |
||||
} |
||||
|
||||
for _, item := range result { |
||||
item = strings.TrimSpace(item) |
||||
if item != "" && !strings.HasPrefix(item, "#") { |
||||
spec := strings.Split(item, " ") |
||||
timer := strings.Join(spec[:5], " ") |
||||
cmd := strings.Join(spec[5:], " ") |
||||
crons = append(crons, cron{timer, cmd}) |
||||
} |
||||
} |
||||
return |
||||
} |
@ -0,0 +1,23 @@
|
||||
package cmd |
||||
|
||||
import ( |
||||
"strings" |
||||
"testing" |
||||
) |
||||
|
||||
func TestCheckCrons(t *testing.T) { |
||||
crontab := ` |
||||
*/1 * * * * /usr/bine/echo hello
|
||||
* * * * * /usr/bin/ls
|
||||
* & * * * /usr/bin/php -v
|
||||
* * * * /usr/bin/go run main.go |
||||
` |
||||
invalidCrons := checkCrons(strings.Split(crontab, "\n")) |
||||
if len(invalidCrons) != 2 { |
||||
t.Error("should have 2 cron expression,but get none.") |
||||
} |
||||
if invalidCrons[0] != "* & * * * /usr/bin/php -v" || |
||||
invalidCrons[1] != "* * * * /usr/bin/go run main.go" { |
||||
t.Error("invalid cron expression should * & * * * /usr/bin/php -v and * * * * /usr/bin/go run main.go.") |
||||
} |
||||
} |
@ -0,0 +1,57 @@
|
||||
package cmd |
||||
|
||||
import ( |
||||
"fmt" |
||||
|
||||
"github.com/shunfei/cronsun" |
||||
"github.com/spf13/cobra" |
||||
) |
||||
|
||||
var all bool |
||||
|
||||
func init() { |
||||
LsCmd.Flags().BoolVarP(&all, "all", "a", false, "list all nodes include not alive") |
||||
} |
||||
|
||||
var LsCmd = &cobra.Command{ |
||||
Use: "ls", |
||||
Short: "list the nodes", |
||||
Run: func(cmd *cobra.Command, args []string) { |
||||
ea := NewExitAction() |
||||
ea.After = func() { |
||||
fmt.Println() |
||||
cmd.Help() |
||||
} |
||||
|
||||
nodes, err := cronsun.GetNodes() |
||||
if err != nil { |
||||
ea.Exit(err.Error()) |
||||
} |
||||
|
||||
fmt.Print("ID") |
||||
for i := 0; i < 5; i++ { |
||||
fmt.Print("\t") |
||||
} |
||||
fmt.Print("ip\t\t\t") |
||||
fmt.Print("pid\t\t") |
||||
fmt.Print("hostname\t") |
||||
fmt.Print("alived\t") |
||||
fmt.Println() |
||||
|
||||
for _, item := range nodes { |
||||
if !all && !item.Alived { |
||||
continue |
||||
} |
||||
fmt.Print(item.ID + "\t") |
||||
fmt.Print(item.IP + "\t\t") |
||||
fmt.Print(item.PID + "\t\t") |
||||
fmt.Print(item.Hostname + "\t\t") |
||||
if item.Alived { |
||||
fmt.Print("Yes" + "\t\t") |
||||
} else { |
||||
fmt.Print("No" + "\t\t") |
||||
} |
||||
|
||||
} |
||||
}, |
||||
} |
Loading…
Reference in new issue