diff --git a/.github/workflows/pal-ci.yml b/.github/workflows/pal-ci.yml index 1b42980..527abf3 100644 --- a/.github/workflows/pal-ci.yml +++ b/.github/workflows/pal-ci.yml @@ -32,12 +32,17 @@ jobs: - name: Build pal Binary run: | make linux + file ./pal + du -sh ./pal + sha256sum ./pal + ./pal -h - name: Build pal Docker Container run: | make certs echo make docker + rm -f ./localhost.* - name: Run Tests Using pal Docker Container run: | diff --git a/README.md b/README.md index 57418d9..beb5f65 100644 --- a/README.md +++ b/README.md @@ -98,8 +98,9 @@ deploy: background: false # Run concurrently (default: false) concurrent: true - # Set action to run to a cron style schedule - cron: "*****" + # Set action to run multiple cron style schedules + crons: + - "*****" # Set command timeout in seconds (default: 600 seconds/10 mins) timeout: 600 # Set custom HTTP Response Headers diff --git a/data/data.go b/data/data.go index 5dcf697..9bb5a73 100644 --- a/data/data.go +++ b/data/data.go @@ -26,7 +26,7 @@ type ActionData struct { Timeout int `yaml:"timeout" json:"timeout" validate:"number"` Cmd string `yaml:"cmd" json:"cmd" validate:"required"` ResponseHeaders []ResponseHeaders `yaml:"headers" json:"headers"` - Cron string `yaml:"cron" json:"cron"` + Crons []string `yaml:"crons" json:"crons"` OnError OnError `yaml:"on_error" json:"on_error"` InputValidate string `yaml:"input_validate" json:"input_validate"` LastRan string `json:"last_ran"` diff --git a/routes/routes.go b/routes/routes.go index a400009..c10fcae 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -84,27 +84,31 @@ func condDisable(group, action string, disabled bool) { if disabled { sched.RemoveByTags(group + action) } else { - if e.Cron != "" && validateInput(e.Cron, "cron") == nil { - var cronDesc string - exprDesc, err := cron.NewDescriptor() - if err == nil { - cronDesc, err = exprDesc.ToDescription(e.Cron, cron.Locale_en) - if err != nil { - cronDesc = "" + if len(e.Crons) > 0 { + for _, c := range e.Crons { + if validateInput(c, "cron") == nil { + var cronDesc string + exprDesc, err := cron.NewDescriptor() + if err == nil { + cronDesc, err = exprDesc.ToDescription(c, cron.Locale_en) + if err != nil { + cronDesc = "" + } + } + + _, err = sched.NewJob( + gocron.CronJob(c, false), + gocron.NewTask(cronTask, resData[group][i]), + gocron.WithName(group+"/"+action), + gocron.WithTags(cronDesc, group+action), + ) + + if err != nil { + // TODOD: log error + return + } } } - - _, err = sched.NewJob( - gocron.CronJob(e.Cron, false), - gocron.NewTask(cronTask, resData[group][i]), - gocron.WithName(group+"/"+action), - gocron.WithTags(cronDesc, group+action), - ) - - if err != nil { - // TODOD: log error - return - } } return } @@ -128,9 +132,9 @@ func getCond(group, action string) bool { } // logError time, error, id, uri fields -func logError(c echo.Context, e error) { +func logError(reqid, uri string, e error) { fmt.Printf("%s\n", fmt.Sprintf(`{"time":"%s","error":"%s","id":"%s","uri":"%s"}`, - utils.TimeNow(config.GetConfigStr("global_timezone")), e.Error(), c.Response().Header().Get(echo.HeaderXRequestID), c.Request().RequestURI)) + utils.TimeNow(config.GetConfigStr("global_timezone")), e.Error(), reqid, uri)) } // RunGroup is the main route for triggering a command @@ -257,7 +261,7 @@ func RunGroup(c echo.Context) error { actionData.LastOutput = err.Error() } mergeGroup(actionData) - logError(c, err) + logError("", "", err) if actionData.OnError.Notification != "" { notification := actionData.OnError.Notification notification = strings.ReplaceAll(notification, "$PAL_GROUP", actionData.Group) @@ -268,10 +272,10 @@ func RunGroup(c echo.Context) error { } err := putNotifications(data.Notification{Group: group, Notification: notification}) if err != nil { - logError(c, err) + logError("", "", err) } } - + return } actionData.Cmd = cmdOrig actionData.Status = "success" @@ -287,8 +291,6 @@ func RunGroup(c echo.Context) error { lock(group, action, false) } - time.Sleep(20 * time.Millisecond) - return c.String(http.StatusOK, "running in background") } @@ -305,7 +307,7 @@ func RunGroup(c echo.Context) error { actionData.LastOutput = err.Error() } mergeGroup(actionData) - logError(c, errors.New(errorScript+" "+err.Error())) + logError(c.Response().Header().Get(echo.HeaderXRequestID), c.Request().RequestURI, errors.New(errorScript+" "+err.Error())) if actionData.OnError.Notification != "" { notification := actionData.OnError.Notification notification = strings.ReplaceAll(notification, "$PAL_GROUP", actionData.Group) @@ -316,7 +318,7 @@ func RunGroup(c echo.Context) error { } err := putNotifications(data.Notification{Group: group, Notification: notification}) if err != nil { - logError(c, err) + logError(c.Response().Header().Get(echo.HeaderXRequestID), c.Request().RequestURI, err) } } return c.String(http.StatusInternalServerError, err.Error()) @@ -688,7 +690,7 @@ func PostFilesUpload(c echo.Context) error { } - return c.HTML(http.StatusOK, fmt.Sprintf("Redirecting...

Successfully uploaded %d files. You will be redirected to /v1/pal/ui/files in 5 seconds...

", len(files))) + return c.HTML(http.StatusOK, fmt.Sprintf("Redirecting...

Successfully uploaded %d files. You will be redirected to /v1/pal/ui/files in 3 seconds...

", len(files))) } @@ -972,24 +974,28 @@ func CronStart(r map[string][]data.ActionData) error { for k, v := range r { for _, e := range v { - if e.Cron != "" && validateInput(e.Cron, "cron") == nil { - var cronDesc string - exprDesc, err := cron.NewDescriptor() - if err == nil { - cronDesc, err = exprDesc.ToDescription(e.Cron, cron.Locale_en) - if err != nil { - cronDesc = "" + if len(e.Crons) > 0 { + for _, c := range e.Crons { + if validateInput(c, "cron") == nil { + var cronDesc string + exprDesc, err := cron.NewDescriptor() + if err == nil { + cronDesc, err = exprDesc.ToDescription(c, cron.Locale_en) + if err != nil { + cronDesc = "" + } + } + _, err = sched.NewJob( + gocron.CronJob(c, false), + gocron.NewTask(cronTask, e), + gocron.WithName(k+"/"+e.Action), + gocron.WithTags(cronDesc, e.Group+e.Action), + ) + if err != nil { + return err + } } } - _, err = sched.NewJob( - gocron.CronJob(e.Cron, false), - gocron.NewTask(cronTask, e), - gocron.WithName(k+"/"+e.Action), - gocron.WithTags(cronDesc, e.Group+e.Action), - ) - if err != nil { - return err - } } } } diff --git a/test/pal-actions.yml b/test/pal-actions.yml index ec14ac7..ea9db1e 100644 --- a/test/pal-actions.yml +++ b/test/pal-actions.yml @@ -15,7 +15,9 @@ test: desc: Test No auth_header and cron auth_header: output: true - cron: "* * * * *" + crons: + - "* * * * *" + - "*/2 * * * *" concurrent: true cmd: echo "$PAL_INPUT no_auth" @@ -24,7 +26,8 @@ test: desc: Test cron at 5 on Sunday auth_header: output: true - cron: "0 5 * * 0" + crons: + - "0 5 * * 0" concurrent: true cmd: echo "$PAL_INPUT sunday_test" @@ -118,6 +121,7 @@ build: make file ./pal du -sh ./pal + sha256sum ./pal ./pal -h json: diff --git a/ui/action.tmpl b/ui/action.tmpl index 44bed21..3f38682 100644 --- a/ui/action.tmpl +++ b/ui/action.tmpl @@ -201,7 +201,9 @@ {{ end }} - {{ $action.Cron }} + {{range $action.Crons}} +

{{ . }}

+ {{ end }} {{range $header := $action.ResponseHeaders}} diff --git a/ui/actions.tmpl b/ui/actions.tmpl index 0a071a0..59aab30 100644 --- a/ui/actions.tmpl +++ b/ui/actions.tmpl @@ -146,7 +146,7 @@ circle {{ end }} - {{ if $action.Cron }} + {{ if $action.Crons }} circle {{ else }} circle diff --git a/ui/files.tmpl b/ui/files.tmpl index 02c338b..7f76044 100644 --- a/ui/files.tmpl +++ b/ui/files.tmpl @@ -88,9 +88,9 @@
Upload Files
-
+
- +