|
@@ -25,15 +25,15 @@ import (
|
|
|
*/
|
|
|
|
|
|
type Job struct {
|
|
|
- Name string //The name of this job
|
|
|
- Creator string //The creator of this job. When execute, this user permission will be used
|
|
|
- Description string //Job description, can be empty
|
|
|
- Admin bool //If the creator has admin permission during the creation of this job. If this doesn't match with the runtime instance, this job wille be skipped
|
|
|
- ExecutionInterval int64 //Execuation interval in seconds
|
|
|
- BaseTime int64 //Exeuction basetime. The next interval is calculated using (current time - base time ) % execution interval
|
|
|
- JobType string //Job type, accept {file/function}. If not set default to file
|
|
|
- ScriptFile string //The script file being called. Can be an agi script (.agi / .js) or shell script (.bat or .sh)
|
|
|
- ScriptFunc func() `json:"-"` //The target function to execute
|
|
|
+ Name string //The name of this job
|
|
|
+ Creator string //The creator of this job. When execute, this user permission will be used
|
|
|
+ Description string //Job description, can be empty
|
|
|
+ Admin bool //If the creator has admin permission during the creation of this job. If this doesn't match with the runtime instance, this job wille be skipped
|
|
|
+ ExecutionInterval int64 //Execuation interval in seconds
|
|
|
+ BaseTime int64 //Exeuction basetime. The next interval is calculated using (current time - base time ) % execution interval
|
|
|
+ JobType string //Job type, accept {file/function}. If not set default to file
|
|
|
+ ScriptFile string //The script file being called. Can be an agi script (.agi / .js) or shell script (.bat or .sh)
|
|
|
+ ScriptFunc func() (string, error) `json:"-"` //The target function to execute
|
|
|
}
|
|
|
|
|
|
type Scheduler struct {
|
|
@@ -122,58 +122,74 @@ func (a *Scheduler) createTicker(duration time.Duration) chan bool {
|
|
|
stop := make(chan bool, 1)
|
|
|
|
|
|
go func() {
|
|
|
- defer log.Println("Aecron Stopped")
|
|
|
+ defer log.Println("Scheduler Stopped")
|
|
|
for {
|
|
|
select {
|
|
|
case <-ticker.C:
|
|
|
//Run jobs
|
|
|
for _, thisJob := range a.jobs {
|
|
|
+ log.Println((time.Now().Unix()-thisJob.BaseTime)%thisJob.ExecutionInterval, thisJob.Name)
|
|
|
if (time.Now().Unix()-thisJob.BaseTime)%thisJob.ExecutionInterval == 0 {
|
|
|
//Execute this job
|
|
|
- scriptFile := thisJob.ScriptFile
|
|
|
- if !fileExists(scriptFile) {
|
|
|
- //This job no longer exists in the file system. Remove it
|
|
|
- a.RemoveJobFromScheduleList(thisJob.Name)
|
|
|
- }
|
|
|
- clonedJobStructure := *thisJob
|
|
|
- ext := filepath.Ext(scriptFile)
|
|
|
- if ext == ".js" || ext == ".agi" {
|
|
|
- //Run using AGI interface in go routine
|
|
|
- go func(thisJob Job) {
|
|
|
- userinfo, err := a.userHandler.GetUserInfoFromUsername(thisJob.Creator)
|
|
|
- if err != nil {
|
|
|
- //This user not exists. Skip this script
|
|
|
- cronlog("[ERROR] User not exists: " + thisJob.Creator + ". Skipping scheduled job: " + thisJob.Name + ".")
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- //Run the script with this user scope
|
|
|
- resp, err := a.gateway.ExecuteAGIScriptAsUser(thisJob.ScriptFile, userinfo)
|
|
|
- if err != nil {
|
|
|
- cronlog("[ERROR] " + thisJob.Name + " " + err.Error())
|
|
|
- } else {
|
|
|
- cronlog(thisJob.Name + " " + resp)
|
|
|
- }
|
|
|
- }(clonedJobStructure)
|
|
|
-
|
|
|
- } else if ext == ".bat" || ext == ".sh" {
|
|
|
- //Run as shell script
|
|
|
- go func(thisJob Job) {
|
|
|
- scriptPath := thisJob.ScriptFile
|
|
|
- if runtime.GOOS == "windows" {
|
|
|
- scriptPath = strings.ReplaceAll(filepath.ToSlash(scriptPath), "/", "\\")
|
|
|
- }
|
|
|
- cmd := exec.Command(scriptPath)
|
|
|
- out, err := cmd.CombinedOutput()
|
|
|
- if err != nil {
|
|
|
- cronlog("[ERROR] " + thisJob.Name + " " + err.Error() + " => " + string(out))
|
|
|
- }
|
|
|
- cronlog(thisJob.Name + " " + string(out))
|
|
|
- }(clonedJobStructure)
|
|
|
+ if thisJob.JobType == "function" {
|
|
|
+ //Execute the script function
|
|
|
+ returnvalue, err := thisJob.ScriptFunc()
|
|
|
+ if err != nil {
|
|
|
+ log.Println(`*Scheduler* Error occured when running task ` + thisJob.Name + ": " + err.Error())
|
|
|
+ cronlog("[ERROR]: " + err.Error())
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ //Execution suceed. Log the return value
|
|
|
+ cronlog(returnvalue)
|
|
|
} else {
|
|
|
- //Unknown script file. Ignore this
|
|
|
- log.Println("This extension is not yet supported: ", ext)
|
|
|
+ //This is requesting to execute a script file
|
|
|
+ scriptFile := thisJob.ScriptFile
|
|
|
+ if !fileExists(scriptFile) {
|
|
|
+ //This job no longer exists in the file system. Remove it
|
|
|
+ a.RemoveJobFromScheduleList(thisJob.Name)
|
|
|
+ }
|
|
|
+ clonedJobStructure := *thisJob
|
|
|
+ ext := filepath.Ext(scriptFile)
|
|
|
+ if ext == ".js" || ext == ".agi" {
|
|
|
+ //Run using AGI interface in go routine
|
|
|
+ go func(thisJob Job) {
|
|
|
+ userinfo, err := a.userHandler.GetUserInfoFromUsername(thisJob.Creator)
|
|
|
+ if err != nil {
|
|
|
+ //This user not exists. Skip this script
|
|
|
+ cronlog("[ERROR] User not exists: " + thisJob.Creator + ". Skipping scheduled job: " + thisJob.Name + ".")
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ //Run the script with this user scope
|
|
|
+ resp, err := a.gateway.ExecuteAGIScriptAsUser(thisJob.ScriptFile, userinfo)
|
|
|
+ if err != nil {
|
|
|
+ cronlog("[ERROR] " + thisJob.Name + " " + err.Error())
|
|
|
+ } else {
|
|
|
+ cronlog(thisJob.Name + " " + resp)
|
|
|
+ }
|
|
|
+ }(clonedJobStructure)
|
|
|
+
|
|
|
+ } else if ext == ".bat" || ext == ".sh" {
|
|
|
+ //Run as shell script
|
|
|
+ go func(thisJob Job) {
|
|
|
+ scriptPath := thisJob.ScriptFile
|
|
|
+ if runtime.GOOS == "windows" {
|
|
|
+ scriptPath = strings.ReplaceAll(filepath.ToSlash(scriptPath), "/", "\\")
|
|
|
+ }
|
|
|
+ cmd := exec.Command(scriptPath)
|
|
|
+ out, err := cmd.CombinedOutput()
|
|
|
+ if err != nil {
|
|
|
+ cronlog("[ERROR] " + thisJob.Name + " " + err.Error() + " => " + string(out))
|
|
|
+ }
|
|
|
+ cronlog(thisJob.Name + " " + string(out))
|
|
|
+ }(clonedJobStructure)
|
|
|
+ } else {
|
|
|
+ //Unknown script file. Ignore this
|
|
|
+ log.Println("This extension is not yet supported: ", ext)
|
|
|
+ }
|
|
|
}
|
|
|
+
|
|
|
}
|
|
|
}
|
|
|
case <-stop:
|
|
@@ -211,7 +227,7 @@ func (a *Scheduler) AddJobToScheduler(job *Job) error {
|
|
|
}
|
|
|
|
|
|
//Create a new scheduled function job in the scheduler
|
|
|
-func (a *Scheduler) CreateNewScheduledFunctionJob(name string, desc string, executionInterval int64, targetFunction func()) error {
|
|
|
+func (a *Scheduler) CreateNewScheduledFunctionJob(name string, desc string, executionInterval int64, targetFunction func() (string, error)) error {
|
|
|
if name == "" || desc == "" {
|
|
|
return errors.New("Name or description of a scheduled task cannot be empty")
|
|
|
}
|
|
@@ -220,6 +236,9 @@ func (a *Scheduler) CreateNewScheduledFunctionJob(name string, desc string, exec
|
|
|
return errors.New("The minimum execution interval is 60 seconds.")
|
|
|
}
|
|
|
|
|
|
+ //Get the cloest minute
|
|
|
+ baseTime := time.Now().Unix() - (time.Now().Unix() % 60)
|
|
|
+
|
|
|
//Create a new scehduled job
|
|
|
newJob := Job{
|
|
|
Name: name,
|
|
@@ -227,7 +246,7 @@ func (a *Scheduler) CreateNewScheduledFunctionJob(name string, desc string, exec
|
|
|
Description: desc,
|
|
|
Admin: true,
|
|
|
ExecutionInterval: executionInterval,
|
|
|
- BaseTime: time.Now().Unix(),
|
|
|
+ BaseTime: baseTime,
|
|
|
JobType: "function",
|
|
|
ScriptFunc: targetFunction,
|
|
|
}
|