WIP: unit test done, need further test
This commit is contained in:
parent
6e84adbf16
commit
61d735bbad
@ -46,7 +46,7 @@ func main() {
|
|||||||
_ = file.Close()
|
_ = file.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if err = wingmate.NewLog(file); err == nil {
|
if err = wingmate.NewLog(file, wingmate.Time|wingmate.Caller); err == nil {
|
||||||
wingmate.Log().Info().Msg(logMessage)
|
wingmate.Log().Info().Msg(logMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -53,7 +53,7 @@ func TestRead(t *testing.T) {
|
|||||||
_ = f.Close()
|
_ = f.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = wingmate.NewLog(os.Stderr)
|
_ = wingmate.NewLog(os.Stderr, wingmate.Caller|wingmate.Time)
|
||||||
tests := []testEntry{
|
tests := []testEntry{
|
||||||
{
|
{
|
||||||
name: "positive",
|
name: "positive",
|
||||||
|
|||||||
@ -20,7 +20,7 @@ func TestCrontab(t *testing.T) {
|
|||||||
wantErr bool
|
wantErr bool
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = wingmate.NewLog(os.Stderr)
|
_ = wingmate.NewLog(os.Stderr, wingmate.Caller|wingmate.Time)
|
||||||
tests := []testEntry{
|
tests := []testEntry{
|
||||||
{
|
{
|
||||||
name: "positive",
|
name: "positive",
|
||||||
|
|||||||
@ -18,7 +18,7 @@ func TestYaml(t *testing.T) {
|
|||||||
wantErr bool
|
wantErr bool
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = wingmate.NewLog(os.Stderr)
|
_ = wingmate.NewLog(os.Stderr, wingmate.Caller|wingmate.Time)
|
||||||
tests := []testEntry{
|
tests := []testEntry{
|
||||||
{
|
{
|
||||||
name: "positive",
|
name: "positive",
|
||||||
|
|||||||
@ -35,10 +35,7 @@ cron:
|
|||||||
goto fail
|
goto fail
|
||||||
}
|
}
|
||||||
cmd = exec.Command(cron.Command(), cron.Arguments()...)
|
cmd = exec.Command(cron.Command(), cron.Arguments()...)
|
||||||
cmd.Env = os.Environ()
|
cmd.Env = cron.PatchEnv(os.Environ())
|
||||||
if cron.EnvLen() > 0 {
|
|
||||||
cmd.Env = append(cmd.Env, cron.Environ()...)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(cron.WorkingDir()) > 0 {
|
if len(cron.WorkingDir()) > 0 {
|
||||||
cmd.Dir = cron.WorkingDir()
|
cmd.Dir = cron.WorkingDir()
|
||||||
|
|||||||
@ -27,6 +27,7 @@ type Task interface {
|
|||||||
Arguments() []string
|
Arguments() []string
|
||||||
EnvLen() int
|
EnvLen() int
|
||||||
Environ() []string
|
Environ() []string
|
||||||
|
PatchEnv([]string) []string
|
||||||
Setsid() bool
|
Setsid() bool
|
||||||
UserGroup() UserGroup
|
UserGroup() UserGroup
|
||||||
WorkingDir() string
|
WorkingDir() string
|
||||||
|
|||||||
@ -40,10 +40,7 @@ service:
|
|||||||
goto fail
|
goto fail
|
||||||
}
|
}
|
||||||
cmd = exec.Command(task.Command(), task.Arguments()...)
|
cmd = exec.Command(task.Command(), task.Arguments()...)
|
||||||
cmd.Env = os.Environ()
|
cmd.Env = task.PatchEnv(os.Environ())
|
||||||
if task.EnvLen() > 0 {
|
|
||||||
cmd.Env = append(cmd.Env, task.Environ()...)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(task.WorkingDir()) > 0 {
|
if len(task.WorkingDir()) > 0 {
|
||||||
cmd.Dir = task.WorkingDir()
|
cmd.Dir = task.WorkingDir()
|
||||||
|
|||||||
@ -19,14 +19,14 @@ func (i *Init) sighandler(wg *sync.WaitGroup, trigger chan<- any, selfExit <-cha
|
|||||||
isOpen := true
|
isOpen := true
|
||||||
|
|
||||||
c := make(chan os.Signal, 1)
|
c := make(chan os.Signal, 1)
|
||||||
signal.Notify(c, unix.SIGINT, unix.SIGTERM, unix.SIGCHLD)
|
signal.Notify(c, unix.SIGINT, unix.SIGTERM, unix.SIGQUIT, unix.SIGCHLD)
|
||||||
|
|
||||||
signal:
|
signal:
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case s := <-c:
|
case s := <-c:
|
||||||
switch s {
|
switch s {
|
||||||
case unix.SIGTERM, unix.SIGINT:
|
case unix.SIGTERM, unix.SIGINT, unix.SIGQUIT:
|
||||||
if isOpen {
|
if isOpen {
|
||||||
wingmate.Log().Info().Msg("initiating shutdown...")
|
wingmate.Log().Info().Msg("initiating shutdown...")
|
||||||
close(trigger)
|
close(trigger)
|
||||||
|
|||||||
@ -249,6 +249,10 @@ func (c *CronTask) Environ() []string {
|
|||||||
return retval
|
return retval
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *CronTask) PatchEnv(env []string) []string {
|
||||||
|
return patchEnv(env, c.environ)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *CronTask) Setsid() bool {
|
func (c *CronTask) Setsid() bool {
|
||||||
return c.setsid
|
return c.setsid
|
||||||
}
|
}
|
||||||
@ -264,7 +268,7 @@ func (c *CronTask) WorkingDir() string {
|
|||||||
func (c *CronTask) Status() wminit.TaskStatus {
|
func (c *CronTask) Status() wminit.TaskStatus {
|
||||||
//TODO: implement me!
|
//TODO: implement me!
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
return nil
|
// return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CronTask) TimeToRun(now time.Time) bool {
|
func (c *CronTask) TimeToRun(now time.Time) bool {
|
||||||
|
|||||||
97
task/task.go
97
task/task.go
@ -13,7 +13,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
envRef = regexp.MustCompile(`\$([a-zA-Z][a-zA-Z0-9]*)`)
|
envCapture = regexp.MustCompile(`\$+[a-zA-Z_][a-zA-Z0-9_]*|\$+{[a-zA-Z_][a-zA-Z0-9_]*}`)
|
||||||
|
envEsc = regexp.MustCompile(`^\$\$+[^\$]+$`) // escaped, starts with two or more $ character
|
||||||
|
envRef = regexp.MustCompile(`^\$([^\$]+)$`) // capture the variable name
|
||||||
|
envRefExplicit = regexp.MustCompile(`^\${([^\$]+)}$`) // capture the variable name - explicit
|
||||||
)
|
)
|
||||||
|
|
||||||
type config interface {
|
type config interface {
|
||||||
@ -75,7 +78,7 @@ func (ts *Tasks) Crones() []wminit.CronTask {
|
|||||||
func (ts *Tasks) Get(name string) (wminit.Task, error) {
|
func (ts *Tasks) Get(name string) (wminit.Task, error) {
|
||||||
//TODO: implement me!
|
//TODO: implement me!
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
return nil, nil
|
// return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServiceTask struct {
|
type ServiceTask struct {
|
||||||
@ -311,32 +314,7 @@ func (t *ServiceTask) Environ() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *ServiceTask) PatchEnv(env []string) []string {
|
func (t *ServiceTask) PatchEnv(env []string) []string {
|
||||||
tMap := make(map[string]string)
|
return patchEnv(env, t.environ)
|
||||||
for _, e := range env {
|
|
||||||
key, value, ok := strings.Cut(e, "=")
|
|
||||||
if !ok {
|
|
||||||
wingmate.Log().Warn().Msgf("removing invalid environment:", e)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
tMap[key] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, e := range t.environ {
|
|
||||||
key, value, ok := strings.Cut(e, "=")
|
|
||||||
if !ok {
|
|
||||||
wingmate.Log().Warn().Msgf("removing invalid environment:", e)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.ContainsAny(key, "$") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
//envRef.FindAllString()
|
|
||||||
_ = value
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *ServiceTask) Setsid() bool {
|
func (t *ServiceTask) Setsid() bool {
|
||||||
@ -358,19 +336,19 @@ func (t *ServiceTask) WorkingDir() string {
|
|||||||
func (t *ServiceTask) Status() wminit.TaskStatus {
|
func (t *ServiceTask) Status() wminit.TaskStatus {
|
||||||
//TODO: implement me!
|
//TODO: implement me!
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
return nil
|
// return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *ServiceTask) AutoStart() bool {
|
func (t *ServiceTask) AutoStart() bool {
|
||||||
//TODO: implement me!
|
//TODO: implement me!
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
return false
|
// return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *ServiceTask) AutoRestart() bool {
|
func (t *ServiceTask) AutoRestart() bool {
|
||||||
//TODO: implement me!
|
//TODO: implement me!
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
return false
|
// return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *ServiceTask) StartSecs() uint {
|
func (t *ServiceTask) StartSecs() uint {
|
||||||
@ -407,3 +385,60 @@ func validate(validators ...func() error) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func patchEnv(existing, new []string) []string {
|
||||||
|
tMap := make(map[string]string)
|
||||||
|
for _, e := range existing {
|
||||||
|
key, value, ok := strings.Cut(e, "=")
|
||||||
|
if !ok {
|
||||||
|
wingmate.Log().Warn().Msgf("removing invalid environment:", e)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
tMap[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range new {
|
||||||
|
key, value, ok := strings.Cut(e, "=")
|
||||||
|
if !ok {
|
||||||
|
wingmate.Log().Warn().Msgf("removing invalid environment:", e)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.ContainsAny(key, "$") {
|
||||||
|
wingmate.Log().Error().Err(fmt.Errorf("variable name contains $")).Msgf("removing invalid environment:", e)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
value = envCapture.ReplaceAllStringFunc(value, func(rep string) string {
|
||||||
|
if envEsc.MatchString(rep) {
|
||||||
|
return rep
|
||||||
|
}
|
||||||
|
|
||||||
|
if envName := envRefExplicit.FindStringSubmatch(rep); envName != nil && envName[1] != "" {
|
||||||
|
exVal, ok := tMap[envName[1]]
|
||||||
|
if !ok {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return exVal
|
||||||
|
}
|
||||||
|
|
||||||
|
if envName := envRef.FindStringSubmatch(rep); envName != nil && envName[1] != "" {
|
||||||
|
exVal, ok := tMap[envName[1]]
|
||||||
|
if !ok {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return exVal
|
||||||
|
}
|
||||||
|
return rep
|
||||||
|
})
|
||||||
|
|
||||||
|
tMap[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
outEnv := make([]string, 0, len(existing))
|
||||||
|
for key, val := range tMap {
|
||||||
|
outEnv = append(outEnv, fmt.Sprintf("%s=%s", key, val))
|
||||||
|
}
|
||||||
|
|
||||||
|
return outEnv
|
||||||
|
}
|
||||||
|
|||||||
@ -1,9 +1,12 @@
|
|||||||
package task
|
package task
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gitea.suyono.dev/suyono/wingmate"
|
||||||
wminit "gitea.suyono.dev/suyono/wingmate/init"
|
wminit "gitea.suyono.dev/suyono/wingmate/init"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"testing"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestServicesV0(t *testing.T) {
|
func TestServicesV0(t *testing.T) {
|
||||||
@ -77,3 +80,49 @@ func TestTasks_List(t *testing.T) {
|
|||||||
|
|
||||||
assert.ElementsMatch(t, testNames, tnames)
|
assert.ElementsMatch(t, testNames, tnames)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestServiceTaskPatchEnv(t *testing.T) {
|
||||||
|
type testEntry struct {
|
||||||
|
name string
|
||||||
|
systemEnv []string
|
||||||
|
serviceEnv []string
|
||||||
|
expected []string
|
||||||
|
}
|
||||||
|
_ = wingmate.NewLog(os.Stderr, wingmate.Caller|wingmate.Time)
|
||||||
|
|
||||||
|
tests := []testEntry{
|
||||||
|
{
|
||||||
|
name: "normal",
|
||||||
|
systemEnv: []string{"PATH=/bin:/usr/bin:/usr/local/bin"},
|
||||||
|
serviceEnv: []string{
|
||||||
|
"SPARK_HOME=/opt/spark",
|
||||||
|
"PATH=$SPARK_HOME/bin:$PATH",
|
||||||
|
},
|
||||||
|
expected: []string{
|
||||||
|
"SPARK_HOME=/opt/spark",
|
||||||
|
"PATH=/opt/spark/bin:/bin:/usr/bin:/usr/local/bin",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "explicit",
|
||||||
|
systemEnv: []string{"PART=hello "},
|
||||||
|
serviceEnv: []string{"GREET=${PART}world"},
|
||||||
|
expected: []string{"PART=hello ", "GREET=hello world"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name+" - service", func(t *testing.T) {
|
||||||
|
st := NewServiceTask(tt.name)
|
||||||
|
st.SetEnv(tt.serviceEnv...)
|
||||||
|
result := st.PatchEnv(tt.systemEnv)
|
||||||
|
assert.ElementsMatch(t, result, tt.expected)
|
||||||
|
})
|
||||||
|
t.Run(tt.name+" - cron", func(t *testing.T) {
|
||||||
|
st := NewCronTask(tt.name)
|
||||||
|
st.SetEnv(tt.serviceEnv...)
|
||||||
|
result := st.PatchEnv(tt.systemEnv)
|
||||||
|
assert.ElementsMatch(t, result, tt.expected)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user