Compare commits
15 Commits
rework
...
ad8499daa5
| Author | SHA1 | Date | |
|---|---|---|---|
| ad8499daa5 | |||
| 8704f80d4b | |||
| dbe9dbba9c | |||
| 4ec5750cd5 | |||
| 3dc69325c1 | |||
| 2eae19f64c | |||
| b589fb8f0c | |||
| dd66cb9f1e | |||
| d5eb872b13 | |||
| 9128503da1 | |||
| d9d1fe72d4 | |||
| 2971f5c709 | |||
| bd4ba67ad2 | |||
| eb7bde3cbe | |||
| a4ba011b36 |
@@ -1,10 +1,24 @@
|
|||||||
{
|
{
|
||||||
"name": "Golang Dev",
|
"name": "Golang Dev",
|
||||||
"image": "golang-dev:1.21-bookworm-user",
|
"image": "golang-dev:1.21-bookworm-user",
|
||||||
|
"mounts": [
|
||||||
|
{
|
||||||
|
"source": "WingmateGoPath",
|
||||||
|
"target": "/go",
|
||||||
|
"type": "volume"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "WingmateGolangDevHome",
|
||||||
|
"target": "/home/golang",
|
||||||
|
"type": "volume"
|
||||||
|
}
|
||||||
|
],
|
||||||
"customizations": {
|
"customizations": {
|
||||||
"vscode": {
|
"vscode": {
|
||||||
"extensions": [
|
"extensions": [
|
||||||
"golang.go"
|
"golang.go",
|
||||||
|
"ms-azuretools.vscode-docker",
|
||||||
|
"ms-vscode.makefile-tools"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,2 +1 @@
|
|||||||
/.idea
|
/cmd/wingmate/wingmate
|
||||||
/wingmate
|
|
||||||
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/wingmate.iml" filepath="$PROJECT_DIR$/.idea/wingmate.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
9
.idea/wingmate.iml
generated
Normal file
9
.idea/wingmate.iml
generated
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="Go" enabled="true" />
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
@@ -1 +1 @@
|
|||||||
golang 1.21.4
|
golang 1.21.5
|
||||||
|
|||||||
29
Makefile
Normal file
29
Makefile
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
|
||||||
|
|
||||||
|
all: wingmate dummy oneshot spawner starter pidproxy
|
||||||
|
|
||||||
|
wingmate:
|
||||||
|
$(MAKE) -C cmd/wingmate all
|
||||||
|
|
||||||
|
pidproxy:
|
||||||
|
$(MAKE) -C cmd/pidproxy all
|
||||||
|
|
||||||
|
dummy:
|
||||||
|
$(MAKE) -C cmd/experiment/dummy all
|
||||||
|
|
||||||
|
oneshot:
|
||||||
|
$(MAKE) -C cmd/experiment/oneshot all
|
||||||
|
|
||||||
|
spawner:
|
||||||
|
$(MAKE) -C cmd/experiment/spawner all
|
||||||
|
|
||||||
|
starter:
|
||||||
|
$(MAKE) -C cmd/experiment/starter all
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(MAKE) -C cmd/wingmate clean
|
||||||
|
$(MAKE) -C cmd/pidproxy clean
|
||||||
|
$(MAKE) -C cmd/experiment/dummy clean
|
||||||
|
$(MAKE) -C cmd/experiment/oneshot clean
|
||||||
|
$(MAKE) -C cmd/experiment/spawner clean
|
||||||
|
$(MAKE) -C cmd/experiment/starter clean
|
||||||
4
cmd/experiment/.gitignore
vendored
Normal file
4
cmd/experiment/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
/dummy/dummy
|
||||||
|
/starter/starter
|
||||||
|
/oneshot/oneshot
|
||||||
|
/spawner/spawner
|
||||||
5
cmd/experiment/dummy/Makefile
Normal file
5
cmd/experiment/dummy/Makefile
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
all:
|
||||||
|
go build -v
|
||||||
|
|
||||||
|
clean:
|
||||||
|
go clean -i -cache -testcache
|
||||||
17
cmd/experiment/dummy/dummy.go
Normal file
17
cmd/experiment/dummy/dummy.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.Println("using log.Println")
|
||||||
|
fmt.Println("using fmt.Println")
|
||||||
|
|
||||||
|
time.Sleep(time.Second * 5)
|
||||||
|
|
||||||
|
log.Println("log: finishing up")
|
||||||
|
fmt.Println("fmt: finishing up")
|
||||||
|
}
|
||||||
5
cmd/experiment/oneshot/Makefile
Normal file
5
cmd/experiment/oneshot/Makefile
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
all:
|
||||||
|
go build -v
|
||||||
|
|
||||||
|
clean:
|
||||||
|
go clean -i -cache -testcache
|
||||||
78
cmd/experiment/oneshot/oneshot.go
Normal file
78
cmd/experiment/oneshot/oneshot.go
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
|
"gitea.suyono.dev/suyono/wingmate"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DummyPath = "/workspaces/wingmate/cmd/experiment/dummy/dummy"
|
||||||
|
DummyPath = "/usr/local/bin/wmdummy"
|
||||||
|
EnvDummyPath = "DUMMY_PATH"
|
||||||
|
EnvLog = "LOG"
|
||||||
|
EnvLogMessage = "LOG_MESSAGE"
|
||||||
|
EnvDefaultLogMessage = "oneshot executed"
|
||||||
|
EnvInstanceNum = "INSTANCE_NUM"
|
||||||
|
EnvDefaultInstances = -1
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
viper.SetEnvPrefix(wingmate.EnvPrefix)
|
||||||
|
viper.BindEnv(EnvDummyPath)
|
||||||
|
viper.BindEnv(EnvLog)
|
||||||
|
viper.BindEnv(EnvLogMessage)
|
||||||
|
viper.BindEnv(EnvInstanceNum)
|
||||||
|
viper.SetDefault(EnvDummyPath, DummyPath)
|
||||||
|
viper.SetDefault(EnvLogMessage, EnvDefaultLogMessage)
|
||||||
|
viper.SetDefault(EnvInstanceNum, EnvDefaultInstances)
|
||||||
|
|
||||||
|
exePath := viper.GetString(EnvDummyPath)
|
||||||
|
|
||||||
|
logPath := viper.GetString(EnvLog)
|
||||||
|
logMessage := viper.GetString(EnvLogMessage)
|
||||||
|
log.Println("log path:", logPath)
|
||||||
|
if logPath != "" {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
file *os.File
|
||||||
|
)
|
||||||
|
|
||||||
|
if file, err = os.OpenFile(logPath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0o666); err == nil {
|
||||||
|
defer func() {
|
||||||
|
_ = file.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err = wingmate.NewLog(file); err == nil {
|
||||||
|
wingmate.Log().Info().Msg(logMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StartInstances(exePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func StartInstances(exePath string) {
|
||||||
|
num := (rand.Uint32() % 16) + 16
|
||||||
|
|
||||||
|
iNum := viper.GetInt(EnvInstanceNum)
|
||||||
|
if iNum > 0 {
|
||||||
|
num = uint32(iNum)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ctr uint32
|
||||||
|
cmd *exec.Cmd
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
for ctr = 0; ctr < num; ctr++ {
|
||||||
|
cmd = exec.Command(exePath)
|
||||||
|
if err = cmd.Start(); err != nil {
|
||||||
|
log.Printf("failed to run %s: %+v\n", exePath, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5
cmd/experiment/spawner/Makefile
Normal file
5
cmd/experiment/spawner/Makefile
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
all:
|
||||||
|
go build -v
|
||||||
|
|
||||||
|
clean:
|
||||||
|
go clean -i -cache -testcache
|
||||||
40
cmd/experiment/spawner/spawner.go
Normal file
40
cmd/experiment/spawner/spawner.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os/exec"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitea.suyono.dev/suyono/wingmate"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
EnvOneShotPath = "ONESHOT_PATH"
|
||||||
|
OneShotPath = "/usr/local/bin/wmoneshot"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var (
|
||||||
|
cmd *exec.Cmd
|
||||||
|
err error
|
||||||
|
t *time.Ticker
|
||||||
|
)
|
||||||
|
viper.SetEnvPrefix(wingmate.EnvPrefix)
|
||||||
|
viper.BindEnv(EnvOneShotPath)
|
||||||
|
viper.SetDefault(EnvOneShotPath, OneShotPath)
|
||||||
|
|
||||||
|
exePath := viper.GetString(EnvOneShotPath)
|
||||||
|
|
||||||
|
t = time.NewTicker(time.Second * 5)
|
||||||
|
for {
|
||||||
|
cmd = exec.Command(exePath)
|
||||||
|
if err = cmd.Run(); err != nil {
|
||||||
|
log.Printf("failed to run %s: %+v\n", exePath, err)
|
||||||
|
} else {
|
||||||
|
log.Printf("%s executed\n", exePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
<-t.C
|
||||||
|
}
|
||||||
|
}
|
||||||
5
cmd/experiment/starter/Makefile
Normal file
5
cmd/experiment/starter/Makefile
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
all:
|
||||||
|
go build -v
|
||||||
|
|
||||||
|
clean:
|
||||||
|
go clean -i -cache -testcache
|
||||||
73
cmd/experiment/starter/starter.go
Normal file
73
cmd/experiment/starter/starter.go
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os/exec"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"gitea.suyono.dev/suyono/wingmate"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DummyPath = "/workspaces/wingmate/cmd/experiment/dummy/dummy"
|
||||||
|
DummyPath = "/usr/local/bin/wmdummy"
|
||||||
|
EnvDummyPath = "DUMMY_PATH"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var (
|
||||||
|
stdout io.ReadCloser
|
||||||
|
stderr io.ReadCloser
|
||||||
|
wg *sync.WaitGroup
|
||||||
|
err error
|
||||||
|
exePath string
|
||||||
|
)
|
||||||
|
viper.SetEnvPrefix(wingmate.EnvPrefix)
|
||||||
|
viper.BindEnv(EnvDummyPath)
|
||||||
|
viper.SetDefault(EnvDummyPath, DummyPath)
|
||||||
|
|
||||||
|
exePath = viper.GetString(EnvDummyPath)
|
||||||
|
|
||||||
|
cmd := exec.Command(exePath)
|
||||||
|
|
||||||
|
if stdout, err = cmd.StdoutPipe(); err != nil {
|
||||||
|
log.Panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if stderr, err = cmd.StderrPipe(); err != nil {
|
||||||
|
log.Panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg = &sync.WaitGroup{}
|
||||||
|
wg.Add(2)
|
||||||
|
go pulley(wg, stdout, "stdout")
|
||||||
|
go pulley(wg, stderr, "stderr")
|
||||||
|
|
||||||
|
if err = cmd.Start(); err != nil {
|
||||||
|
log.Panic(err)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
if err = cmd.Wait(); err != nil {
|
||||||
|
log.Printf("got error when Waiting for child process: %#v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func pulley(wg *sync.WaitGroup, src io.ReadCloser, srcName string) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(src)
|
||||||
|
for scanner.Scan() {
|
||||||
|
log.Printf("coming out from %s: %s\n", srcName, scanner.Text())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
log.Printf("got error whean reading from %s: %#v\n", srcName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("closing %s...\n", srcName)
|
||||||
|
_ = src.Close()
|
||||||
|
}
|
||||||
5
cmd/pidproxy/Makefile
Normal file
5
cmd/pidproxy/Makefile
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
all:
|
||||||
|
go build -v
|
||||||
|
|
||||||
|
clean:
|
||||||
|
go clean -i -cache -testcache
|
||||||
142
cmd/pidproxy/pidproxy.go
Normal file
142
cmd/pidproxy/pidproxy.go
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strconv"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitea.suyono.dev/suyono/wingmate"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
pidFileFlag = "pid-file"
|
||||||
|
EnvStartSecs = "STARTSECS"
|
||||||
|
EnvDefaultStartSecs = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
rootCmd = &cobra.Command{
|
||||||
|
Use: "wmpidproxy",
|
||||||
|
RunE: pidProxy,
|
||||||
|
}
|
||||||
|
|
||||||
|
childArgs []string
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var (
|
||||||
|
i int
|
||||||
|
arg string
|
||||||
|
selfArgs []string
|
||||||
|
found bool
|
||||||
|
)
|
||||||
|
|
||||||
|
viper.SetEnvPrefix(wingmate.EnvPrefix)
|
||||||
|
viper.BindEnv(EnvStartSecs)
|
||||||
|
viper.SetDefault(EnvStartSecs, EnvDefaultStartSecs)
|
||||||
|
|
||||||
|
if len(os.Args) <= 2 {
|
||||||
|
log.Println("invalid argument")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
rootCmd.PersistentFlags().StringP(pidFileFlag, "p", "", "location of pid file")
|
||||||
|
rootCmd.MarkFlagRequired(pidFileFlag)
|
||||||
|
viper.BindPFlag(pidFileFlag, rootCmd.PersistentFlags().Lookup(pidFileFlag))
|
||||||
|
|
||||||
|
found = false
|
||||||
|
for i, arg = range os.Args {
|
||||||
|
if arg == "--" {
|
||||||
|
found = true
|
||||||
|
selfArgs = os.Args[1:i]
|
||||||
|
if len(os.Args) <= i+1 {
|
||||||
|
log.Println("invalid argument")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
childArgs = os.Args[i+1:]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
log.Println("invalid argument")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
rootCmd.SetArgs(selfArgs)
|
||||||
|
|
||||||
|
if err := rootCmd.Execute(); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func pidProxy(cmd *cobra.Command, args []string) error {
|
||||||
|
pidfile := viper.GetString(pidFileFlag)
|
||||||
|
log.Printf("%s %v", pidfile, childArgs)
|
||||||
|
if len(childArgs) > 1 {
|
||||||
|
go startProcess(childArgs[0], childArgs[1:]...)
|
||||||
|
} else {
|
||||||
|
go startProcess(childArgs[0])
|
||||||
|
}
|
||||||
|
initialWait := viper.GetInt(EnvStartSecs)
|
||||||
|
time.Sleep(time.Second * time.Duration(initialWait))
|
||||||
|
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
pid int
|
||||||
|
)
|
||||||
|
for {
|
||||||
|
if pid, err = readPid(pidfile); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = unix.Kill(pid, syscall.Signal(0)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readPid(pidFile string) (int, error) {
|
||||||
|
var (
|
||||||
|
file *os.File
|
||||||
|
err error
|
||||||
|
buf []byte
|
||||||
|
n int
|
||||||
|
pid64 int64
|
||||||
|
)
|
||||||
|
|
||||||
|
if file, err = os.Open(pidFile); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = file.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
buf = make([]byte, 1024)
|
||||||
|
n, err = file.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pid64, err = strconv.ParseInt(string(buf[:n]), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(pid64), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func startProcess(arg0 string, args ...string) {
|
||||||
|
if err := exec.Command(arg0, args...).Run(); err != nil {
|
||||||
|
log.Println("exec:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
5
cmd/wingmate/Makefile
Normal file
5
cmd/wingmate/Makefile
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
all:
|
||||||
|
go build -v
|
||||||
|
|
||||||
|
clean:
|
||||||
|
go clean -i -cache -testcache
|
||||||
@@ -54,7 +54,7 @@ func Read() (*Config, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wingmate.Log().Error().Msgf("encounter error when reading service directory %s: %#v", svcdir, err)
|
wingmate.Log().Error().Msgf("encounter error when reading service directory %s: %+v", svcdir, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
crontabfile = filepath.Join(configPath, CrontabFileName)
|
crontabfile = filepath.Join(configPath, CrontabFileName)
|
||||||
@@ -64,7 +64,7 @@ func Read() (*Config, error) {
|
|||||||
cronAvailable = true
|
cronAvailable = true
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wingmate.Log().Error().Msgf("encounter error when reading crontab %s: %#v", crontabfile, err)
|
wingmate.Log().Error().Msgf("encounter error when reading crontab %s: %+v", crontabfile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !serviceAvailable && !cronAvailable {
|
if !serviceAvailable && !cronAvailable {
|
||||||
|
|||||||
@@ -28,14 +28,6 @@ type CronTimeSpec interface {
|
|||||||
Match(uint8) bool
|
Match(uint8) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// type Cron interface {
|
|
||||||
// Minute() CronTimeSpec
|
|
||||||
// Hour() CronTimeSpec
|
|
||||||
// DayOfMonth() CronTimeSpec
|
|
||||||
// Month() CronTimeSpec
|
|
||||||
// DayOfWeek() CronTimeSpec
|
|
||||||
// }
|
|
||||||
|
|
||||||
type Cron struct {
|
type Cron struct {
|
||||||
minute CronTimeSpec
|
minute CronTimeSpec
|
||||||
hour CronTimeSpec
|
hour CronTimeSpec
|
||||||
@@ -97,27 +89,27 @@ func readCrontab(path string) ([]*Cron, error) {
|
|||||||
hasRun: false,
|
hasRun: false,
|
||||||
}
|
}
|
||||||
if err = c.setField(minute, parts[1]); err != nil {
|
if err = c.setField(minute, parts[1]); err != nil {
|
||||||
wingmate.Log().Error().Msgf("error parsing minute field %#v", err)
|
wingmate.Log().Error().Msgf("error parsing minute field %+v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = c.setField(hour, parts[2]); err != nil {
|
if err = c.setField(hour, parts[2]); err != nil {
|
||||||
wingmate.Log().Error().Msgf("error parsing hour field %#v", err)
|
wingmate.Log().Error().Msgf("error parsing hour field %+v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = c.setField(dom, parts[3]); err != nil {
|
if err = c.setField(dom, parts[3]); err != nil {
|
||||||
wingmate.Log().Error().Msgf("error parsing day of month field %#v", err)
|
wingmate.Log().Error().Msgf("error parsing day of month field %+v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = c.setField(month, parts[4]); err != nil {
|
if err = c.setField(month, parts[4]); err != nil {
|
||||||
wingmate.Log().Error().Msgf("error parsing month field %#v", err)
|
wingmate.Log().Error().Msgf("error parsing month field %+v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = c.setField(dow, parts[5]); err != nil {
|
if err = c.setField(dow, parts[5]); err != nil {
|
||||||
wingmate.Log().Error().Msgf("error parsing day of week field %#v", err)
|
wingmate.Log().Error().Msgf("error parsing day of week field %+v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,22 +126,25 @@ func (c *Cron) Command() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cron) TimeToRun(now time.Time) bool {
|
func (c *Cron) TimeToRun(now time.Time) bool {
|
||||||
if !c.hasRun {
|
|
||||||
c.lastRun = now
|
|
||||||
c.hasRun = true
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if now.Sub(c.lastRun) <= time.Minute && now.Minute() == c.lastRun.Minute() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.minute.Match(uint8(now.Minute())) &&
|
if c.minute.Match(uint8(now.Minute())) &&
|
||||||
c.hour.Match(uint8(now.Hour())) &&
|
c.hour.Match(uint8(now.Hour())) &&
|
||||||
c.dom.Match(uint8(now.Day())) &&
|
c.dom.Match(uint8(now.Day())) &&
|
||||||
c.month.Match(uint8(now.Month())) &&
|
c.month.Match(uint8(now.Month())) &&
|
||||||
c.dow.Match(uint8(now.Weekday())) {
|
c.dow.Match(uint8(now.Weekday())) {
|
||||||
return true
|
|
||||||
|
if c.hasRun {
|
||||||
|
if now.Sub(c.lastRun) <= time.Minute && now.Minute() == c.lastRun.Minute() {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
c.lastRun = now
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
c.lastRun = now
|
||||||
|
c.hasRun = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
@@ -208,12 +203,12 @@ func (c *Cron) setField(field cronField, input string) error {
|
|||||||
*cField = &specAny{}
|
*cField = &specAny{}
|
||||||
} else if strings.HasPrefix(input, "*/") {
|
} else if strings.HasPrefix(input, "*/") {
|
||||||
if parsed64, err = strconv.ParseUint(input[2:], 10, 8); err != nil {
|
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)
|
return fmt.Errorf("error parse field %+v with input %s: %w", field, input, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
parsed = uint8(parsed64)
|
parsed = uint8(parsed64)
|
||||||
if fr.valid(parsed) {
|
if !fr.valid(parsed) {
|
||||||
return fmt.Errorf("error parse field %#v with input %s: invalid value", field, input)
|
return fmt.Errorf("error parse field %+v with input %s parsed to %d: invalid value", field, input, parsed)
|
||||||
}
|
}
|
||||||
multi = make([]uint8, 0)
|
multi = make([]uint8, 0)
|
||||||
current = parsed
|
current = parsed
|
||||||
@@ -231,12 +226,12 @@ func (c *Cron) setField(field cronField, input string) error {
|
|||||||
multi = make([]uint8, 0)
|
multi = make([]uint8, 0)
|
||||||
for _, s := range multiStr {
|
for _, s := range multiStr {
|
||||||
if parsed64, err = strconv.ParseUint(s, 10, 8); err != nil {
|
if parsed64, err = strconv.ParseUint(s, 10, 8); err != nil {
|
||||||
return fmt.Errorf("error parse field %#v with input %s: %w", field, input, err)
|
return fmt.Errorf("error parse field %+v with input %s: %w", field, input, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
parsed = uint8(parsed64)
|
parsed = uint8(parsed64)
|
||||||
if fr.valid(parsed) {
|
if !fr.valid(parsed) {
|
||||||
return fmt.Errorf("error parse field %#v with input %s: invalid value", field, input)
|
return fmt.Errorf("error parse field %+v with input %s: invalid value", field, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
multi = append(multi, parsed)
|
multi = append(multi, parsed)
|
||||||
@@ -247,12 +242,12 @@ func (c *Cron) setField(field cronField, input string) error {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if parsed64, err = strconv.ParseUint(input, 10, 8); err != nil {
|
if parsed64, err = strconv.ParseUint(input, 10, 8); err != nil {
|
||||||
return fmt.Errorf("error parse field %#v with input %s: %w", field, input, err)
|
return fmt.Errorf("error parse field %+v with input %s: %w", field, input, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
parsed = uint8(parsed64)
|
parsed = uint8(parsed64)
|
||||||
if fr.valid(parsed) {
|
if !fr.valid(parsed) {
|
||||||
return fmt.Errorf("error parse field %#v with input %s: invalid value", field, input)
|
return fmt.Errorf("error parse field %+v with input %s: invalid value", field, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
*cField = &specExact{
|
*cField = &specExact{
|
||||||
|
|||||||
22
docker/alpine/Dockerfile
Normal file
22
docker/alpine/Dockerfile
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
FROM golang:1.21-alpine as builder
|
||||||
|
|
||||||
|
ADD . /root/wingmate
|
||||||
|
WORKDIR /root/wingmate/
|
||||||
|
RUN apk add make && make all
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
FROM alpine:3.18
|
||||||
|
|
||||||
|
RUN apk add tzdata && ln -s /usr/share/zoneinfo/Australia/Sydney /etc/localtime
|
||||||
|
COPY --from=builder /root/wingmate/cmd/wingmate/wingmate /usr/local/bin/wingmate
|
||||||
|
COPY --from=builder /root/wingmate/cmd/experiment/dummy/dummy /usr/local/bin/wmdummy
|
||||||
|
COPY --from=builder /root/wingmate/cmd/experiment/starter/starter /usr/local/bin/wmstarter
|
||||||
|
COPY --from=builder /root/wingmate/cmd/experiment/oneshot/oneshot /usr/local/bin/wmoneshot
|
||||||
|
COPY --from=builder /root/wingmate/cmd/experiment/spawner/spawner /usr/local/bin/wmspawner
|
||||||
|
COPY --from=builder /root/wingmate/cmd/pidproxy/pidproxy /usr/local/bin/wmpidproxy
|
||||||
|
ADD --chmod=755 docker/alpine/entry.sh /usr/local/bin/entry.sh
|
||||||
|
ADD --chmod=755 docker/alpine/etc /etc
|
||||||
|
|
||||||
|
ENTRYPOINT [ "/usr/local/bin/entry.sh" ]
|
||||||
|
CMD [ "/usr/local/bin/wingmate" ]
|
||||||
7
docker/alpine/entry.sh
Normal file
7
docker/alpine/entry.sh
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if [ $# -gt 0 ]; then
|
||||||
|
exec "$@"
|
||||||
|
else
|
||||||
|
exec /usr/local/bin/wingmate
|
||||||
|
fi
|
||||||
3
docker/alpine/etc/wingmate/crontab
Normal file
3
docker/alpine/etc/wingmate/crontab
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
*/5 * * * * /etc/wingmate/crontab.d/cron1.sh
|
||||||
|
17,42 */2 * * * /etc/wingmate/crontab.d/cron2.sh
|
||||||
|
7,19,23,47 22 * * * /etc/wingmate/crontab.d/cron3.sh
|
||||||
9
docker/alpine/etc/wingmate/crontab.d/cron1.sh
Normal file
9
docker/alpine/etc/wingmate/crontab.d/cron1.sh
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
export WINGMATE_DUMMY_PATH=/usr/local/bin/wmdummy
|
||||||
|
export WINGMATE_LOG=/var/log/cron1.log
|
||||||
|
export WINGMATE_LOG_MESSAGE="cron executed in minute 5,10,15,20,25,30,35,40,45,50,55"
|
||||||
|
|
||||||
|
echo "I'm runnig with dummy=$WINGMATE_DUMMY_PATH, log=$WINGMATE_LOG and mesage=$WINGMATE_LOG_MESSAGE" >> /var/log/debug-cron.log
|
||||||
|
|
||||||
|
exec /usr/local/bin/wmoneshot
|
||||||
7
docker/alpine/etc/wingmate/crontab.d/cron2.sh
Normal file
7
docker/alpine/etc/wingmate/crontab.d/cron2.sh
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
export WINGMATE_DUMMY_PATH=/usr/local/bin/wmdummy
|
||||||
|
export WINGMATE_LOG=/var/log/cron2.log
|
||||||
|
export WINGMATE_LOG_MESSAGE="cron scheduled using 17,42 */2 * * *"
|
||||||
|
|
||||||
|
exec /usr/local/bin/wmoneshot
|
||||||
7
docker/alpine/etc/wingmate/crontab.d/cron3.sh
Normal file
7
docker/alpine/etc/wingmate/crontab.d/cron3.sh
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
export WINGMATE_DUMMY_PATH=/usr/local/bin/wmdummy
|
||||||
|
export WINGMATE_LOG=/var/log/cron3.log
|
||||||
|
export WINGMATE_LOG_MESSAGE="cron entry: 7,19,23,47 22 * * * /etc/wingmate/crontab.d/cron3.sh"
|
||||||
|
|
||||||
|
exec /usr/local/bin/wmoneshot
|
||||||
4
docker/alpine/etc/wingmate/service/one.sh
Normal file
4
docker/alpine/etc/wingmate/service/one.sh
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
export DUMMY_PATH=/usr/local/bin/wmdummy
|
||||||
|
exec /usr/local/bin/wmstarter
|
||||||
5
docker/alpine/etc/wingmate/service/spawner.sh
Normal file
5
docker/alpine/etc/wingmate/service/spawner.sh
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
export WINGMATE_ONESHOT_PATH=/usr/local/bin/wmoneshot
|
||||||
|
export WINGMATE_DUMMY_PATH=/usr/local/bin/wmdummy
|
||||||
|
exec /usr/local/bin/wmspawner
|
||||||
22
docker/bookworm/Dockerfile
Normal file
22
docker/bookworm/Dockerfile
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
FROM golang:1.21-bookworm as builder
|
||||||
|
|
||||||
|
ADD . /root/wingmate
|
||||||
|
WORKDIR /root/wingmate/
|
||||||
|
RUN make all
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
FROM debian:bookworm
|
||||||
|
|
||||||
|
RUN ln -sf /usr/share/zoneinfo/Australia/Sydney /etc/localtime
|
||||||
|
COPY --from=builder /root/wingmate/cmd/wingmate/wingmate /usr/local/bin/wingmate
|
||||||
|
COPY --from=builder /root/wingmate/cmd/experiment/dummy/dummy /usr/local/bin/wmdummy
|
||||||
|
COPY --from=builder /root/wingmate/cmd/experiment/starter/starter /usr/local/bin/wmstarter
|
||||||
|
COPY --from=builder /root/wingmate/cmd/experiment/oneshot/oneshot /usr/local/bin/wmoneshot
|
||||||
|
COPY --from=builder /root/wingmate/cmd/experiment/spawner/spawner /usr/local/bin/wmspawner
|
||||||
|
COPY --from=builder /root/wingmate/cmd/pidproxy/pidproxy /usr/local/bin/wmpidproxy
|
||||||
|
ADD --chmod=755 docker/bookworm/entry.sh /usr/local/bin/entry.sh
|
||||||
|
ADD --chmod=755 docker/bookworm/etc /etc
|
||||||
|
|
||||||
|
ENTRYPOINT [ "/usr/local/bin/entry.sh" ]
|
||||||
|
CMD [ "/usr/local/bin/wingmate" ]
|
||||||
7
docker/bookworm/entry.sh
Normal file
7
docker/bookworm/entry.sh
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/bash
|
||||||
|
|
||||||
|
if [ $# -gt 0 ]; then
|
||||||
|
exec "$@"
|
||||||
|
else
|
||||||
|
exec /usr/local/bin/wingmate
|
||||||
|
fi
|
||||||
3
docker/bookworm/etc/wingmate/crontab
Normal file
3
docker/bookworm/etc/wingmate/crontab
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
*/5 * * * * /etc/wingmate/crontab.d/cron1.sh
|
||||||
|
17,42 */2 * * * /etc/wingmate/crontab.d/cron2.sh
|
||||||
|
7,19,23,47 22 * * * /etc/wingmate/crontab.d/cron3.sh
|
||||||
9
docker/bookworm/etc/wingmate/crontab.d/cron1.sh
Normal file
9
docker/bookworm/etc/wingmate/crontab.d/cron1.sh
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/bash
|
||||||
|
|
||||||
|
export WINGMATE_DUMMY_PATH=/usr/local/bin/wmdummy
|
||||||
|
export WINGMATE_LOG=/var/log/cron1.log
|
||||||
|
export WINGMATE_LOG_MESSAGE="cron executed in minute 5,10,15,20,25,30,35,40,45,50,55"
|
||||||
|
|
||||||
|
echo "I'm runnig with dummy=$WINGMATE_DUMMY_PATH, log=$WINGMATE_LOG and mesage=$WINGMATE_LOG_MESSAGE" >> /var/log/debug-cron.log
|
||||||
|
|
||||||
|
exec /usr/local/bin/wmoneshot
|
||||||
7
docker/bookworm/etc/wingmate/crontab.d/cron2.sh
Normal file
7
docker/bookworm/etc/wingmate/crontab.d/cron2.sh
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/bash
|
||||||
|
|
||||||
|
export WINGMATE_DUMMY_PATH=/usr/local/bin/wmdummy
|
||||||
|
export WINGMATE_LOG=/var/log/cron2.log
|
||||||
|
export WINGMATE_LOG_MESSAGE="cron scheduled using 17,42 */2 * * *"
|
||||||
|
|
||||||
|
exec /usr/local/bin/wmoneshot
|
||||||
7
docker/bookworm/etc/wingmate/crontab.d/cron3.sh
Normal file
7
docker/bookworm/etc/wingmate/crontab.d/cron3.sh
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/bash
|
||||||
|
|
||||||
|
export WINGMATE_DUMMY_PATH=/usr/local/bin/wmdummy
|
||||||
|
export WINGMATE_LOG=/var/log/cron3.log
|
||||||
|
export WINGMATE_LOG_MESSAGE="cron entry: 7,19,23,47 22 * * * /etc/wingmate/crontab.d/cron3.sh"
|
||||||
|
|
||||||
|
exec /usr/local/bin/wmoneshot
|
||||||
4
docker/bookworm/etc/wingmate/service/one.sh
Normal file
4
docker/bookworm/etc/wingmate/service/one.sh
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/bash
|
||||||
|
|
||||||
|
export DUMMY_PATH=/usr/local/bin/wmdummy
|
||||||
|
exec /usr/local/bin/wmstarter
|
||||||
5
docker/bookworm/etc/wingmate/service/spawner.sh
Normal file
5
docker/bookworm/etc/wingmate/service/spawner.sh
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/bash
|
||||||
|
|
||||||
|
export WINGMATE_ONESHOT_PATH=/usr/local/bin/wmoneshot
|
||||||
|
export WINGMATE_DUMMY_PATH=/usr/local/bin/wmdummy
|
||||||
|
exec /usr/local/bin/wmspawner
|
||||||
6
go.mod
6
go.mod
@@ -3,8 +3,8 @@ module gitea.suyono.dev/suyono/wingmate
|
|||||||
go 1.21
|
go 1.21
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/rs/zerolog v1.30.0
|
github.com/rs/zerolog v1.31.0
|
||||||
github.com/spf13/cobra v1.7.0
|
github.com/spf13/cobra v1.8.0
|
||||||
github.com/spf13/viper v1.17.0
|
github.com/spf13/viper v1.17.0
|
||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.8.4
|
||||||
golang.org/x/sys v0.15.0
|
golang.org/x/sys v0.15.0
|
||||||
@@ -17,7 +17,7 @@ require (
|
|||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/magiconair/properties v1.8.7 // indirect
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
|
|||||||
7
go.sum
7
go.sum
@@ -48,6 +48,7 @@ github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnht
|
|||||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -150,6 +151,8 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k
|
|||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
|
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
|
||||||
@@ -166,6 +169,8 @@ github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZV
|
|||||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
github.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c=
|
github.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c=
|
||||||
github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w=
|
github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w=
|
||||||
|
github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
|
||||||
|
github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
||||||
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
||||||
@@ -183,6 +188,8 @@ github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
|||||||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||||
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
|
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
|
||||||
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
||||||
|
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
||||||
|
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
|
|||||||
49
init/cron.go
49
init/cron.go
@@ -1,6 +1,7 @@
|
|||||||
package init
|
package init
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -8,19 +9,61 @@ import (
|
|||||||
"gitea.suyono.dev/suyono/wingmate"
|
"gitea.suyono.dev/suyono/wingmate"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
cronTag = "cron"
|
||||||
|
)
|
||||||
|
|
||||||
func (i *Init) cron(wg *sync.WaitGroup, cron Cron, exitFlag <-chan any) {
|
func (i *Init) cron(wg *sync.WaitGroup, cron Cron, exitFlag <-chan any) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
ticker := time.NewTicker(time.Second * 20)
|
var (
|
||||||
|
iwg *sync.WaitGroup
|
||||||
|
err error
|
||||||
|
stdout io.ReadCloser
|
||||||
|
stderr io.ReadCloser
|
||||||
|
)
|
||||||
|
|
||||||
|
ticker := time.NewTicker(time.Second * 30)
|
||||||
cron:
|
cron:
|
||||||
for {
|
for {
|
||||||
if cron.TimeToRun(time.Now()) {
|
if cron.TimeToRun(time.Now()) {
|
||||||
|
wingmate.Log().Info().Str(cronTag, cron.Command().Path()).Msg("executing")
|
||||||
cmd := exec.Command(cron.Command().Path())
|
cmd := exec.Command(cron.Command().Path())
|
||||||
if err := cmd.Run(); err != nil {
|
iwg = &sync.WaitGroup{}
|
||||||
wingmate.Log().Error().Msgf("running cron %s error %#v", cron.Command().Path(), err)
|
|
||||||
|
if stdout, err = cmd.StdoutPipe(); err != nil {
|
||||||
|
wingmate.Log().Error().Str(cronTag, cron.Command().Path()).Msgf("stdout pipe: %+v", err)
|
||||||
|
goto fail
|
||||||
|
}
|
||||||
|
|
||||||
|
if stderr, err = cmd.StderrPipe(); err != nil {
|
||||||
|
wingmate.Log().Error().Str(cronTag, cron.Command().Path()).Msgf("stderr pipe: %+v", err)
|
||||||
|
_ = stdout.Close()
|
||||||
|
goto fail
|
||||||
|
}
|
||||||
|
|
||||||
|
iwg.Add(1)
|
||||||
|
go i.pipeReader(iwg, stdout, cronTag, cron.Command().Path())
|
||||||
|
|
||||||
|
iwg.Add(1)
|
||||||
|
go i.pipeReader(iwg, stderr, cronTag, cron.Command().Path())
|
||||||
|
|
||||||
|
if err := cmd.Start(); err != nil {
|
||||||
|
wingmate.Log().Error().Msgf("starting cron %s error %+v", cron.Command().Path(), err)
|
||||||
|
_ = stdout.Close()
|
||||||
|
_ = stderr.Close()
|
||||||
|
iwg.Wait()
|
||||||
|
goto fail
|
||||||
|
}
|
||||||
|
|
||||||
|
iwg.Wait()
|
||||||
|
|
||||||
|
if err = cmd.Wait(); err != nil {
|
||||||
|
wingmate.Log().Error().Str(cronTag, cron.Command().Path()).Msgf("got error when waiting: %+v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fail:
|
||||||
select {
|
select {
|
||||||
case <-exitFlag:
|
case <-exitFlag:
|
||||||
ticker.Stop()
|
ticker.Stop()
|
||||||
|
|||||||
@@ -1,26 +1,76 @@
|
|||||||
package init
|
package init
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"io"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"gitea.suyono.dev/suyono/wingmate"
|
"gitea.suyono.dev/suyono/wingmate"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
serviceTag = "service"
|
||||||
|
)
|
||||||
|
|
||||||
func (i *Init) service(wg *sync.WaitGroup, path Path, exitFlag <-chan any) {
|
func (i *Init) service(wg *sync.WaitGroup, path Path, exitFlag <-chan any) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
|
iwg *sync.WaitGroup
|
||||||
|
stderr io.ReadCloser
|
||||||
|
stdout io.ReadCloser
|
||||||
|
failStatus bool
|
||||||
)
|
)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
wingmate.Log().Info().Str(serviceTag, path.Path()).Msg("stopped")
|
||||||
|
}()
|
||||||
|
|
||||||
service:
|
service:
|
||||||
for {
|
for {
|
||||||
|
failStatus = false
|
||||||
cmd := exec.Command(path.Path())
|
cmd := exec.Command(path.Path())
|
||||||
if err = cmd.Run(); err != nil {
|
iwg = &sync.WaitGroup{}
|
||||||
|
|
||||||
|
if stdout, err = cmd.StdoutPipe(); err != nil {
|
||||||
|
wingmate.Log().Error().Str(serviceTag, path.Path()).Msgf("stdout pipe: %#v", err)
|
||||||
|
failStatus = true
|
||||||
|
goto fail
|
||||||
|
}
|
||||||
|
iwg.Add(1)
|
||||||
|
go i.pipeReader(iwg, stdout, serviceTag, path.Path())
|
||||||
|
|
||||||
|
if stderr, err = cmd.StderrPipe(); err != nil {
|
||||||
|
wingmate.Log().Error().Str(serviceTag, path.Path()).Msgf("stderr pipe: %#v", err)
|
||||||
|
_ = stdout.Close()
|
||||||
|
failStatus = true
|
||||||
|
goto fail
|
||||||
|
}
|
||||||
|
iwg.Add(1)
|
||||||
|
go i.pipeReader(iwg, stderr, serviceTag, path.Path())
|
||||||
|
|
||||||
|
if err = cmd.Start(); err != nil {
|
||||||
wingmate.Log().Error().Msgf("starting service %s error %#v", path.Path(), err)
|
wingmate.Log().Error().Msgf("starting service %s error %#v", path.Path(), err)
|
||||||
|
failStatus = true
|
||||||
|
_ = stdout.Close()
|
||||||
|
_ = stderr.Close()
|
||||||
|
iwg.Wait()
|
||||||
|
goto fail
|
||||||
}
|
}
|
||||||
|
|
||||||
|
iwg.Wait()
|
||||||
|
|
||||||
|
if err = cmd.Wait(); err != nil {
|
||||||
|
wingmate.Log().Error().Str(serviceTag, path.Path()).Msgf("got error when waiting: %+v", err)
|
||||||
|
}
|
||||||
|
fail:
|
||||||
|
if failStatus {
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
failStatus = false
|
||||||
|
}
|
||||||
select {
|
select {
|
||||||
case <-exitFlag:
|
case <-exitFlag:
|
||||||
break service
|
break service
|
||||||
@@ -29,3 +79,18 @@ service:
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Init) pipeReader(wg *sync.WaitGroup, pipe io.ReadCloser, tag, serviceName string) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(pipe)
|
||||||
|
for scanner.Scan() {
|
||||||
|
wingmate.Log().Info().Str(tag, serviceName).Msg(scanner.Text())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
wingmate.Log().Error().Str(tag, serviceName).Msgf("got error when reading pipe: %#v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wingmate.Log().Info().Str(tag, serviceName).Msg("closing pipe")
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,11 +5,16 @@ import (
|
|||||||
"os/signal"
|
"os/signal"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"gitea.suyono.dev/suyono/wingmate"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (i *Init) sighandler(wg *sync.WaitGroup, trigger chan<- any, selfExit <-chan any) {
|
func (i *Init) sighandler(wg *sync.WaitGroup, trigger chan<- any, selfExit <-chan any) {
|
||||||
defer wg.Wait()
|
defer wg.Done()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
wingmate.Log().Warn().Msg("signal handler: exiting")
|
||||||
|
}()
|
||||||
|
|
||||||
isOpen := true
|
isOpen := true
|
||||||
|
|
||||||
@@ -23,7 +28,10 @@ signal:
|
|||||||
switch s {
|
switch s {
|
||||||
case unix.SIGTERM, unix.SIGINT:
|
case unix.SIGTERM, unix.SIGINT:
|
||||||
if isOpen {
|
if isOpen {
|
||||||
|
wingmate.Log().Info().Msg("initiating shutdown...")
|
||||||
close(trigger)
|
close(trigger)
|
||||||
|
wg.Add(1)
|
||||||
|
go i.signalPump(wg, selfExit)
|
||||||
isOpen = false
|
isOpen = false
|
||||||
}
|
}
|
||||||
case unix.SIGCHLD:
|
case unix.SIGCHLD:
|
||||||
@@ -31,6 +39,7 @@ signal:
|
|||||||
}
|
}
|
||||||
|
|
||||||
case <-selfExit:
|
case <-selfExit:
|
||||||
|
wingmate.Log().Warn().Msg("signal handler received completion flag")
|
||||||
break signal
|
break signal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
71
init/signal-pump.go
Normal file
71
init/signal-pump.go
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
package init
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitea.suyono.dev/suyono/wingmate"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
type status int
|
||||||
|
|
||||||
|
const (
|
||||||
|
triggered status = iota
|
||||||
|
expired
|
||||||
|
)
|
||||||
|
|
||||||
|
func (i *Init) signalPump(wg *sync.WaitGroup, selfExit <-chan any) {
|
||||||
|
defer wg.Done()
|
||||||
|
defer func() {
|
||||||
|
wingmate.Log().Info().Msg("signal pump completed")
|
||||||
|
}()
|
||||||
|
|
||||||
|
if seStatus := i.sigTermPump(time.Now(), selfExit); seStatus == triggered {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
i.sigKillPump(time.Now(), selfExit)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Init) sigKillPump(startTime time.Time, selfExit <-chan any) {
|
||||||
|
t := time.NewTicker(time.Millisecond * 200)
|
||||||
|
defer t.Stop()
|
||||||
|
|
||||||
|
wingmate.Log().Info().Msg("start pumping SIGKILL signal")
|
||||||
|
defer func() {
|
||||||
|
wingmate.Log().Info().Msg("stop pumping SIGKILL signal")
|
||||||
|
}()
|
||||||
|
|
||||||
|
for time.Since(startTime) < time.Second {
|
||||||
|
_ = unix.Kill(-1, unix.SIGKILL)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-t.C:
|
||||||
|
case <-selfExit:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Init) sigTermPump(startTime time.Time, selfExit <-chan any) status {
|
||||||
|
t := time.NewTicker(time.Millisecond * 100)
|
||||||
|
defer t.Stop()
|
||||||
|
|
||||||
|
wingmate.Log().Info().Msg("start pumping SIGTERM signal")
|
||||||
|
defer func() {
|
||||||
|
wingmate.Log().Info().Msg("stop pumping SIGTERM signal")
|
||||||
|
}()
|
||||||
|
|
||||||
|
for time.Since(startTime) < time.Duration(time.Second*4) {
|
||||||
|
_ = unix.Kill(-1, unix.SIGTERM)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-t.C:
|
||||||
|
case <-selfExit:
|
||||||
|
return triggered
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return expired
|
||||||
|
}
|
||||||
@@ -3,7 +3,9 @@ package init
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitea.suyono.dev/suyono/wingmate"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -13,24 +15,42 @@ func (i *Init) waiter(wg *sync.WaitGroup, runningFlag <-chan any, sigHandlerFlag
|
|||||||
// pid int
|
// pid int
|
||||||
err error
|
err error
|
||||||
running bool
|
running bool
|
||||||
|
flagged bool
|
||||||
)
|
)
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
wingmate.Log().Info().Msg("waiter exiting...")
|
||||||
|
}()
|
||||||
|
|
||||||
running = true
|
running = true
|
||||||
|
flagged = true
|
||||||
wait:
|
wait:
|
||||||
for {
|
for {
|
||||||
select {
|
if running {
|
||||||
case <-runningFlag:
|
select {
|
||||||
running = false
|
case <-runningFlag:
|
||||||
default:
|
wingmate.Log().Info().Msg("waiter received shutdown signal...")
|
||||||
|
running = false
|
||||||
|
default:
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = unix.Wait4(-1, &ws, 0, nil); err != nil {
|
if _, err = unix.Wait4(-1, &ws, 0, nil); err != nil {
|
||||||
if errors.Is(err, unix.ECHILD) {
|
if errors.Is(err, unix.ECHILD) {
|
||||||
if !running {
|
if !running {
|
||||||
close(sigHandlerFlag)
|
if flagged {
|
||||||
|
close(sigHandlerFlag)
|
||||||
|
flagged = false
|
||||||
|
wingmate.Log().Warn().Msg("waiter: inner flag")
|
||||||
|
}
|
||||||
|
wingmate.Log().Warn().Msg("waiter: no child left")
|
||||||
break wait
|
break wait
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wingmate.Log().Warn().Msgf("Wait4 returns error: %+v", err)
|
||||||
|
time.Sleep(time.Millisecond * 100)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
29
logger.go
29
logger.go
@@ -1,9 +1,15 @@
|
|||||||
package wingmate
|
package wingmate
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
|
||||||
"gitea.suyono.dev/suyono/wingmate/logger"
|
"gitea.suyono.dev/suyono/wingmate/logger"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"io"
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
timeTag = "time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -29,13 +35,28 @@ func Log() logger.Log {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *wrapper) Info() logger.Content {
|
func (w *wrapper) Info() logger.Content {
|
||||||
return w.log.Info()
|
return (*eventWrapper)(w.log.Info().Time(timeTag, time.Now()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *wrapper) Warn() logger.Content {
|
func (w *wrapper) Warn() logger.Content {
|
||||||
return w.log.Warn()
|
return (*eventWrapper)(w.log.Warn().Time(timeTag, time.Now()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *wrapper) Error() logger.Content {
|
func (w *wrapper) Error() logger.Content {
|
||||||
return w.log.Error()
|
return (*eventWrapper)(w.log.Error().Time(timeTag, time.Now()))
|
||||||
|
}
|
||||||
|
|
||||||
|
type eventWrapper zerolog.Event
|
||||||
|
|
||||||
|
func (w *eventWrapper) Msg(msg string) {
|
||||||
|
(*zerolog.Event)(w).Msg(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *eventWrapper) Msgf(format string, data ...any) {
|
||||||
|
(*zerolog.Event)(w).Msgf(format, data...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *eventWrapper) Str(key, value string) logger.Content {
|
||||||
|
rv := (*zerolog.Event)(w).Str(key, value)
|
||||||
|
return (*eventWrapper)(rv)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package logger
|
|||||||
type Content interface {
|
type Content interface {
|
||||||
Msg(string)
|
Msg(string)
|
||||||
Msgf(string, ...any)
|
Msgf(string, ...any)
|
||||||
|
Str(string, string) Content
|
||||||
}
|
}
|
||||||
|
|
||||||
type Level interface {
|
type Level interface {
|
||||||
|
|||||||
Reference in New Issue
Block a user