Browse Source

skeleton ready. TODO: add context with timeout, commands execution, right ticktack function

tags/version-1.0
Vladimir Smagin 8 months ago
parent
commit
30f4e12b20
5 changed files with 107 additions and 16 deletions
  1. 1
    0
      .gitignore
  2. 2
    1
      configs/test1.yml
  3. 1
    1
      file_utils.go
  4. BIN
      gogocron
  5. 103
    14
      gogocron.go

+ 1
- 0
.gitignore View File

@@ -0,0 +1 @@
gogocron

+ 2
- 1
configs/test1.yml View File

@@ -1,6 +1,7 @@
---
name: "Print base64 of 20 random symbols"
runSecond: "*/5"
runsecond: "*/5"
timeout: 1
commands:
- "head -c 20 /dev/urandom |base64"
- ls

+ 1
- 1
file_utils.go View File

@@ -33,7 +33,7 @@ func readConfigFile(path string) cronTask {
log.Fatalf("Error opening file %v", err)
}

log.Printf(string(yamldata))
//log.Printf(string(yamldata))

marshErr := yaml.Unmarshal(yamldata, &config)
if marshErr != nil {

BIN
gogocron View File


+ 103
- 14
gogocron.go View File

@@ -2,28 +2,117 @@ package main

import (
"log"
"math"
"strconv"
"strings"
"time"
)

type cronTask struct {
name string `yaml:"name"`
runSecond string `yaml:"runSecond,omitempty"`
runMinute string `yaml:"runMinute,omitempty"`
runHour string `yaml:"runHour,omitempty"`
runDom string `yaml:"runDom,omitempty"`
runMonth string `yaml:"runMonth,omitempty"`
runDow string `yaml:"runDow,omitempty"`
commands []string `yaml:"commands"`
Name string `yaml:"name"` //name of task
RunSecond string `yaml:"runsecond,omitempty"` // second
RunMinute string `yaml:"runminute,omitempty"` // minute
RunHour string `yaml:"runhour,omitempty"` // hour
RunDom string `yaml:"rundom,omitempty"` // day of month
RunMonth string `yaml:"runmonth,omitempty"` // month
RunDow string `yaml:"rundow,omitempty"` // day of week
TimeOut int `yaml:"timeout,omitempty"` // exec with timeout, seconds
Commands []string `yaml:"commands"` // array of commands to exec
}

type cronTasks []cronTask

func loadConfig() {
configs := readConfigDirectory("configs")
log.Printf("Files found: %v", configs)
config := readConfigFile(configs[0])
log.Printf("Parsed %#v", config)
// loadConfig() searches for config files and reading them to cronTasks
// used once at start and every HUP signal
// TODO: add HUP signal
func loadConfig() cronTasks {
var tasks cronTasks

configFiles := readConfigDirectory("configs")
log.Printf("Files found: %v", configFiles)
for _, configFile := range configFiles {
config := readConfigFile(configFile)
log.Printf("Parsed %#v", config)
tasks = append(tasks, config)
}
return tasks
}

// tick tack tick tack tick tack tick tack
func ticktack(tickchan chan struct{}) {
for {
// TODO: this code is wrong, but enough for tests
// rewrite to calculate seconds change
time.Sleep(time.Second)
tickchan <- struct{}{}
}
}

// isReadyToExec accepting cron value like "*/5" and time value like current second or hour
func isReadyToExec(cronValue string, timeValue int) bool {
run := false
if cronValue != "" {
// check for * mask
if strings.Contains(cronValue, "*") {
// yes, contains. compute now often to run.
if cronValue == "*" {
// run every second
run = true
}
if strings.Contains(cronValue, "/") {
// slash found, compute time to run
split := strings.Split(cronValue, "/")
divider, _ := strconv.ParseFloat(split[1], 10)
if v := math.Mod(float64(timeValue), divider); v == 0 {
run = true
}
}
} else {
// no, searching for integers and arrays of integers
}
}
return run
}

// search for tasks that must be executed now
func filterTasksToExecute(tasks cronTasks) cronTasks {
now := time.Now()
var tasksToExecute cronTasks
for _, task := range tasks {
// calculate seconds
if isReadyToExec(task.RunSecond, now.Second()) {
tasksToExecute = append(tasksToExecute, task)
}
}
return tasksToExecute
}

// executeTasks runs batch of planned tasks on go routines with context
// TODO: add execute function with context
// TODO: add context.timeout if task.Timeout present
func executeTasks(tasks cronTasks) {
now := time.Now()
log.Printf("Execute started: %v", now)
for _, task := range tasks {
log.Printf("\ttask: %#v", task.Name)
log.Printf("\t\tcommands: %#v", task.Commands)
}

}

func main() {
loadConfig()
tasks := loadConfig()
tickchan := make(chan struct{})
go ticktack(tickchan)

for {
select {
case <-tickchan:
tasksToExecute := filterTasksToExecute(tasks)
if len(tasksToExecute) > 0 {
go executeTasks(tasksToExecute)
}
}
}

}

Loading…
Cancel
Save