Compare commits
No commits in common. "a2f7dbca8216e9fbaacd493a217622537df07130" and "6dd0a8007c3a16ab83e8b2a0f8fe5d125f81d581" have entirely different histories.
a2f7dbca82
...
6dd0a8007c
@ -49,4 +49,5 @@ func convertSchedule(cfg config.CronTimeSpec) task.CronTimeSpec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
panic("invalid conversion")
|
panic("invalid conversion")
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"gitea.suyono.dev/suyono/wingmate"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gitea.suyono.dev/suyono/wingmate"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -97,11 +96,11 @@ const crontabTestCase3 = `a 13 3,5,7 * * /path/to/executable
|
|||||||
*/5 13 a * * /path/to/executable
|
*/5 13 a * * /path/to/executable
|
||||||
*/5 13 3,5,7 a * /path/to/executable
|
*/5 13 3,5,7 a * /path/to/executable
|
||||||
*/5 13 3,5,7 * a /path/to/executable
|
*/5 13 3,5,7 * a /path/to/executable
|
||||||
*/x 13 3,5,7 * * /path/to/executable
|
*/x 13 3,5,7 * a /path/to/executable
|
||||||
76 13 3,5,7 * * /path/to/executable
|
76 13 3,5,7 * a /path/to/executable
|
||||||
*/75 13 3,5,7 * * /path/to/executable
|
*/75 13 3,5,7 * a /path/to/executable
|
||||||
*/5 13 3,x,7 * * /path/to/executable
|
*/5 13 3,x,7 * a /path/to/executable
|
||||||
*/5 13 3,5,67 * * /path/to/executable
|
*/5 13 3,5,67 * a /path/to/executable
|
||||||
*/5 13 * * /path/to/executable
|
*/5 13 * * /path/to/executable
|
||||||
*/5 13 3,5,7 * * /path/to/executable`
|
*/5 13 3,5,7 * * /path/to/executable`
|
||||||
|
|
||||||
|
|||||||
149
config/yaml.go
149
config/yaml.go
@ -1,27 +1,16 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"gitea.suyono.dev/suyono/wingmate"
|
"gitea.suyono.dev/suyono/wingmate"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CrontabScheduleRegexPattern = `^\s*(?P<minute>\S+)\s+(?P<hour>\S+)\s+(?P<dom>\S+)\s+(?P<month>\S+)\s+(?P<dow>\S+)\s*$`
|
ServiceConfigGroup = "service"
|
||||||
CrontabScheduleSubMatchLen = 6
|
CronConfigGroup = "cron"
|
||||||
ServiceConfigGroup = "service"
|
ServiceKeyFormat = "service.%s"
|
||||||
CronConfigGroup = "cron"
|
CronKeyFormat = "cron.%s"
|
||||||
ServiceKeyFormat = "service.%s"
|
|
||||||
CronKeyFormat = "cron.%s"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
crontabScheduleRegex = regexp.MustCompile(CrontabScheduleRegexPattern)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func readConfigYaml(path, name, format string) ([]ServiceTask, []CronTask, error) {
|
func readConfigYaml(path, name, format string) ([]ServiceTask, []CronTask, error) {
|
||||||
@ -65,138 +54,8 @@ func readConfigYaml(path, name, format string) ([]ServiceTask, []CronTask, error
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
cronTask.Name = itemName
|
cronTask.Name = itemName
|
||||||
if cronTask.CronSchedule, err = parseYamlSchedule(cronTask.Schedule); err != nil {
|
|
||||||
wingmate.Log().Error().Msgf("parsing cron schedule: %+v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
crones = append(crones, cronTask)
|
crones = append(crones, cronTask)
|
||||||
}
|
}
|
||||||
|
|
||||||
return services, crones, nil
|
return services, crones, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseYamlSchedule(input string) (schedule CronSchedule, err error) {
|
|
||||||
var (
|
|
||||||
parts []string
|
|
||||||
pSched *CronSchedule
|
|
||||||
)
|
|
||||||
|
|
||||||
parts = crontabScheduleRegex.FindStringSubmatch(input)
|
|
||||||
if len(parts) != CrontabScheduleSubMatchLen {
|
|
||||||
return schedule, fmt.Errorf("invalid schedule: %s", input)
|
|
||||||
}
|
|
||||||
|
|
||||||
pSched = &schedule
|
|
||||||
if err = pSched.setField(minute, parts[1]); err != nil {
|
|
||||||
return schedule, fmt.Errorf("error parsing Minute field: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = pSched.setField(hour, parts[2]); err != nil {
|
|
||||||
return schedule, fmt.Errorf("error parsing Hour field: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = pSched.setField(dom, parts[3]); err != nil {
|
|
||||||
return schedule, fmt.Errorf("error parsing Day of Month field: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = pSched.setField(month, parts[4]); err != nil {
|
|
||||||
return schedule, fmt.Errorf("error parsing Month field: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = pSched.setField(dow, parts[5]); err != nil {
|
|
||||||
return schedule, fmt.Errorf("error parsing Day of Week field: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CronSchedule) setField(field cronField, input string) error {
|
|
||||||
var (
|
|
||||||
fr *fieldRange
|
|
||||||
cField *CronTimeSpec
|
|
||||||
err error
|
|
||||||
parsed64 uint64
|
|
||||||
parsed uint8
|
|
||||||
multi []uint8
|
|
||||||
current uint8
|
|
||||||
multiStr []string
|
|
||||||
)
|
|
||||||
switch field {
|
|
||||||
case minute:
|
|
||||||
fr = newRange(0, 59)
|
|
||||||
cField = &c.Minute
|
|
||||||
case hour:
|
|
||||||
fr = newRange(0, 23)
|
|
||||||
cField = &c.Hour
|
|
||||||
case dom:
|
|
||||||
fr = newRange(1, 31)
|
|
||||||
cField = &c.DoM
|
|
||||||
case month:
|
|
||||||
fr = newRange(1, 12)
|
|
||||||
cField = &c.Month
|
|
||||||
case dow:
|
|
||||||
fr = newRange(0, 6)
|
|
||||||
cField = &c.DoW
|
|
||||||
default:
|
|
||||||
return errors.New("invalid cron field descriptor")
|
|
||||||
}
|
|
||||||
|
|
||||||
if input == "*" {
|
|
||||||
*cField = &SpecAny{}
|
|
||||||
} else if strings.HasPrefix(input, "*/") {
|
|
||||||
if parsed64, err = strconv.ParseUint(input[2:], 10, 8); err != nil {
|
|
||||||
return fmt.Errorf("error parse field %+v with input %s: %w", field, input, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
parsed = uint8(parsed64)
|
|
||||||
if !fr.valid(parsed) {
|
|
||||||
return fmt.Errorf("error parse field %+v with input %s parsed to %d: invalid value", field, input, parsed)
|
|
||||||
}
|
|
||||||
multi = make([]uint8, 0)
|
|
||||||
current = parsed
|
|
||||||
for fr.valid(current) {
|
|
||||||
multi = append(multi, current)
|
|
||||||
current += parsed
|
|
||||||
}
|
|
||||||
|
|
||||||
*cField = &SpecMultiOccurrence{
|
|
||||||
values: multi,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
multiStr = strings.Split(input, ",")
|
|
||||||
if len(multiStr) > 1 {
|
|
||||||
multi = make([]uint8, 0)
|
|
||||||
for _, s := range multiStr {
|
|
||||||
if parsed64, err = strconv.ParseUint(s, 10, 8); err != nil {
|
|
||||||
return fmt.Errorf("error parse field %+v with input %s: %w", field, input, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
parsed = uint8(parsed64)
|
|
||||||
if !fr.valid(parsed) {
|
|
||||||
return fmt.Errorf("error parse field %+v with input %s: invalid value", field, input)
|
|
||||||
}
|
|
||||||
|
|
||||||
multi = append(multi, parsed)
|
|
||||||
}
|
|
||||||
|
|
||||||
*cField = &SpecMultiOccurrence{
|
|
||||||
values: multi,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if parsed64, err = strconv.ParseUint(input, 10, 8); err != nil {
|
|
||||||
return fmt.Errorf("error parse field %+v with input %s: %w", field, input, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
parsed = uint8(parsed64)
|
|
||||||
if !fr.valid(parsed) {
|
|
||||||
return fmt.Errorf("error parse field %+v with input %s: invalid value", field, input)
|
|
||||||
}
|
|
||||||
|
|
||||||
*cField = &SpecExact{
|
|
||||||
value: parsed,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,12 +1,10 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"gitea.suyono.dev/suyono/wingmate"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gitea.suyono.dev/suyono/wingmate"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const configName = "wingmate.yaml"
|
const configName = "wingmate.yaml"
|
||||||
@ -25,31 +23,7 @@ func TestYaml(t *testing.T) {
|
|||||||
config: yamlTestCase0,
|
config: yamlTestCase0,
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "service only",
|
|
||||||
config: yamlTestCase1,
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "cron only",
|
|
||||||
config: yamlTestCase2,
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid content - service",
|
|
||||||
config: yamlTestCase3,
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tc := range yamlBlobs {
|
|
||||||
tests = append(tests, testEntry{
|
|
||||||
name: fmt.Sprintf("negative - %d", i),
|
|
||||||
config: tc,
|
|
||||||
wantErr: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
setup(t)
|
setup(t)
|
||||||
@ -85,25 +59,6 @@ func writeYaml(t *testing.T, path, content string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const yamlTestCase0 = `version: "1"
|
const yamlTestCase0 = `version: "1"
|
||||||
service:
|
|
||||||
one:
|
|
||||||
command: ["command", "arg0", "arg1"]
|
|
||||||
environ: ["ENV1=value1", "ENV2=valueX"]
|
|
||||||
user: "user1"
|
|
||||||
group: "999"
|
|
||||||
working_dir: "/path/to/working"
|
|
||||||
cron:
|
|
||||||
cron-one:
|
|
||||||
command:
|
|
||||||
- command-cron
|
|
||||||
- arg0
|
|
||||||
- arg1
|
|
||||||
environ: ["ENV1=v1", "ENV2=var2"]
|
|
||||||
user: "1001"
|
|
||||||
group: "978"
|
|
||||||
schedule: "*/5 * * * 2,3"`
|
|
||||||
|
|
||||||
const yamlTestCase1 = `version: "1"
|
|
||||||
service:
|
service:
|
||||||
one:
|
one:
|
||||||
command: ["command", "arg0", "arg1"]
|
command: ["command", "arg0", "arg1"]
|
||||||
@ -111,148 +66,3 @@ service:
|
|||||||
user: "user1"
|
user: "user1"
|
||||||
group: "999"
|
group: "999"
|
||||||
working_dir: "/path/to/working"`
|
working_dir: "/path/to/working"`
|
||||||
|
|
||||||
const yamlTestCase2 = `version: "1"
|
|
||||||
cron:
|
|
||||||
cron-one:
|
|
||||||
command:
|
|
||||||
- command-cron
|
|
||||||
- arg0
|
|
||||||
- arg1
|
|
||||||
environ: ["ENV1=v1", "ENV2=var2"]
|
|
||||||
user: "1001"
|
|
||||||
group: "978"
|
|
||||||
schedule: "*/5 * * * 2,3"`
|
|
||||||
|
|
||||||
const yamlTestCase3 = `version: "1"
|
|
||||||
service:
|
|
||||||
one:
|
|
||||||
command: 12345
|
|
||||||
environ: ["ENV1=value1", "ENV2=valueX"]
|
|
||||||
user: "user1"
|
|
||||||
group: "999"
|
|
||||||
working_dir: "/path/to/working"`
|
|
||||||
|
|
||||||
var yamlBlobs = []string{
|
|
||||||
`version: "1"
|
|
||||||
cron:
|
|
||||||
cron-one:
|
|
||||||
command:
|
|
||||||
- command-cron
|
|
||||||
- arg0
|
|
||||||
- arg1
|
|
||||||
environ: ["ENV1=v1", "ENV2=var2"]
|
|
||||||
user: "1001"
|
|
||||||
group: "978"
|
|
||||||
schedule: "a 13 3,5,7 * *"`,
|
|
||||||
`version: "1"
|
|
||||||
cron:
|
|
||||||
cron-one:
|
|
||||||
command:
|
|
||||||
- command-cron
|
|
||||||
- arg0
|
|
||||||
- arg1
|
|
||||||
environ: ["ENV1=v1", "ENV2=var2"]
|
|
||||||
user: "1001"
|
|
||||||
group: "978"
|
|
||||||
schedule: "*/5 a 3,5,7 * *"`,
|
|
||||||
`version: "1"
|
|
||||||
cron:
|
|
||||||
cron-one:
|
|
||||||
command:
|
|
||||||
- command-cron
|
|
||||||
- arg0
|
|
||||||
- arg1
|
|
||||||
environ: ["ENV1=v1", "ENV2=var2"]
|
|
||||||
user: "1001"
|
|
||||||
group: "978"
|
|
||||||
schedule: "*/5 13 a * *"`,
|
|
||||||
`version: "1"
|
|
||||||
cron:
|
|
||||||
cron-one:
|
|
||||||
command:
|
|
||||||
- command-cron
|
|
||||||
- arg0
|
|
||||||
- arg1
|
|
||||||
environ: ["ENV1=v1", "ENV2=var2"]
|
|
||||||
user: "1001"
|
|
||||||
group: "978"
|
|
||||||
schedule: "*/5 13 3,5,7 a *"`,
|
|
||||||
`version: "1"
|
|
||||||
cron:
|
|
||||||
cron-one:
|
|
||||||
command:
|
|
||||||
- command-cron
|
|
||||||
- arg0
|
|
||||||
- arg1
|
|
||||||
environ: ["ENV1=v1", "ENV2=var2"]
|
|
||||||
user: "1001"
|
|
||||||
group: "978"
|
|
||||||
schedule: "*/5 13 3,5,7 * a"`,
|
|
||||||
`version: "1"
|
|
||||||
cron:
|
|
||||||
cron-one:
|
|
||||||
command:
|
|
||||||
- command-cron
|
|
||||||
- arg0
|
|
||||||
- arg1
|
|
||||||
environ: ["ENV1=v1", "ENV2=var2"]
|
|
||||||
user: "1001"
|
|
||||||
group: "978"
|
|
||||||
schedule: "*/x 13 3,5,7 * *"`,
|
|
||||||
`version: "1"
|
|
||||||
cron:
|
|
||||||
cron-one:
|
|
||||||
command:
|
|
||||||
- command-cron
|
|
||||||
- arg0
|
|
||||||
- arg1
|
|
||||||
environ: ["ENV1=v1", "ENV2=var2"]
|
|
||||||
user: "1001"
|
|
||||||
group: "978"
|
|
||||||
schedule: "76 13 3,5,7 * *"`,
|
|
||||||
`version: "1"
|
|
||||||
cron:
|
|
||||||
cron-one:
|
|
||||||
command:
|
|
||||||
- command-cron
|
|
||||||
- arg0
|
|
||||||
- arg1
|
|
||||||
environ: ["ENV1=v1", "ENV2=var2"]
|
|
||||||
user: "1001"
|
|
||||||
group: "978"
|
|
||||||
schedule: "*/75 13 3,5,7 * *"`,
|
|
||||||
`version: "1"
|
|
||||||
cron:
|
|
||||||
cron-one:
|
|
||||||
command:
|
|
||||||
- command-cron
|
|
||||||
- arg0
|
|
||||||
- arg1
|
|
||||||
environ: ["ENV1=v1", "ENV2=var2"]
|
|
||||||
user: "1001"
|
|
||||||
group: "978"
|
|
||||||
schedule: "*/5 13 3,x,7 * *"`,
|
|
||||||
`version: "1"
|
|
||||||
cron:
|
|
||||||
cron-one:
|
|
||||||
command:
|
|
||||||
- command-cron
|
|
||||||
- arg0
|
|
||||||
- arg1
|
|
||||||
environ: ["ENV1=v1", "ENV2=var2"]
|
|
||||||
user: "1001"
|
|
||||||
group: "978"
|
|
||||||
schedule: "*/5 13 3,5,67 * *"`,
|
|
||||||
`version: "1"
|
|
||||||
cron:
|
|
||||||
cron-one:
|
|
||||||
command:
|
|
||||||
- command-cron
|
|
||||||
- arg0
|
|
||||||
- arg1
|
|
||||||
environ: ["ENV1=v1", "ENV2=var2"]
|
|
||||||
user: "1001"
|
|
||||||
group: "978"
|
|
||||||
schedule: "*/5 13 * *"`,
|
|
||||||
}
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user