fix(splitargs): wrong code, check should be outside of loop
feat(config): added WMPidProxyCheckVersion and WMExecCheckVersion to the interface; mutex for accessing viper fixed(docker/bookworm-newconfig): golang version and config path feat(UtilDepCheck): added utility dependency check before running the task
This commit is contained in:
parent
a63646aab2
commit
7db6f6f8f3
|
@ -2,10 +2,9 @@ package cli
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
func SplitArgs() ([]string, []string, error) {
|
||||
func SplitArgs(args []string) ([]string, []string, error) {
|
||||
var (
|
||||
i int
|
||||
arg string
|
||||
|
@ -13,25 +12,25 @@ func SplitArgs() ([]string, []string, error) {
|
|||
childArgs []string
|
||||
)
|
||||
found := false
|
||||
for i, arg = range os.Args {
|
||||
for i, arg = range args {
|
||||
if arg == "--" {
|
||||
found = true
|
||||
if i+1 == len(os.Args) {
|
||||
if i+1 == len(args) {
|
||||
return nil, nil, errors.New("invalid argument")
|
||||
}
|
||||
|
||||
if len(os.Args[i+1:]) == 0 {
|
||||
if len(args[i+1:]) == 0 {
|
||||
return nil, nil, errors.New("invalid argument")
|
||||
}
|
||||
|
||||
selfArgs = os.Args[1:i]
|
||||
childArgs = os.Args[i+1:]
|
||||
selfArgs = args[1:i]
|
||||
childArgs = args[i+1:]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
return nil, nil, errors.New("invalid argument")
|
||||
}
|
||||
if !found {
|
||||
return nil, nil, errors.New("invalid argument")
|
||||
}
|
||||
return selfArgs, childArgs, nil
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package cli
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestSplitArgs(t *testing.T) {
|
||||
SplitArgs([]string{"wmexec", "--user", "1200", "--", "wmspawner"})
|
||||
}
|
|
@ -71,7 +71,7 @@ func main() {
|
|||
|
||||
app.version.Cmd(rootCmd)
|
||||
|
||||
selfArgs, childArgs, err = cli.SplitArgs()
|
||||
selfArgs, childArgs, err = cli.SplitArgs(os.Args)
|
||||
app.childArgs = childArgs
|
||||
app.err = err
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ func main() {
|
|||
app.version.Flag(rootCmd)
|
||||
app.version.Cmd(rootCmd)
|
||||
|
||||
selfArgs, childArgs, err = cli.SplitArgs()
|
||||
selfArgs, childArgs, err = cli.SplitArgs(os.Args)
|
||||
app.childArgs = childArgs
|
||||
app.err = err
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ func convert(cfg *config.Config) *wConfig {
|
|||
|
||||
ct := task.NewCronTask(c.Name).SetCommand(c.Command...).SetEnv(c.Environ...)
|
||||
ct.SetFlagSetsid(c.Setsid).SetWorkingDir(c.WorkingDir).SetUser(c.User).SetGroup(c.Group)
|
||||
ct.SetSchedule(schedule)
|
||||
ct.SetSchedule(c.Schedule, schedule)
|
||||
ct.SetConfig(cfg)
|
||||
|
||||
retval.tasks.AddCron(ct)
|
||||
|
|
|
@ -2,6 +2,7 @@ package config
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
@ -108,6 +109,7 @@ func Read() (*Config, error) {
|
|||
serviceAvailable = false
|
||||
cronAvailable = false
|
||||
outConfig := &Config{
|
||||
viperMtx: &sync.Mutex{},
|
||||
ServiceV0: make([]string, 0),
|
||||
}
|
||||
configPath := viper.GetString(ConfigPath)
|
||||
|
@ -160,6 +162,13 @@ func Read() (*Config, error) {
|
|||
return outConfig, nil
|
||||
}
|
||||
|
||||
func (c *Config) GetAppVersion() string {
|
||||
c.viperMtx.Lock()
|
||||
defer c.viperMtx.Unlock()
|
||||
|
||||
return viper.GetString(WingmateVersion)
|
||||
}
|
||||
|
||||
func (c *Config) WMPidProxyPath() string {
|
||||
c.viperMtx.Lock()
|
||||
defer c.viperMtx.Unlock()
|
||||
|
@ -167,9 +176,46 @@ func (c *Config) WMPidProxyPath() string {
|
|||
return viper.GetString(PidProxyPathConfig)
|
||||
}
|
||||
|
||||
func (c *Config) WMPidProxyCheckVersion() error {
|
||||
var (
|
||||
binVersion string
|
||||
appVersion string
|
||||
err error
|
||||
)
|
||||
|
||||
if binVersion, err = getVersion(c.WMPidProxyPath()); err != nil {
|
||||
return fmt.Errorf("get wmpidproxy version: %w", err)
|
||||
}
|
||||
|
||||
appVersion = c.GetAppVersion()
|
||||
if appVersion != binVersion {
|
||||
return fmt.Errorf("wmpidproxy version mismatch")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Config) WMExecPath() string {
|
||||
c.viperMtx.Lock()
|
||||
defer c.viperMtx.Unlock()
|
||||
|
||||
return viper.GetString(ExecPathConfig)
|
||||
}
|
||||
|
||||
func (c *Config) WMExecCheckVersion() error {
|
||||
var (
|
||||
binVersion string
|
||||
appVersion string
|
||||
err error
|
||||
)
|
||||
|
||||
if binVersion, err = getVersion(c.WMExecPath()); err != nil {
|
||||
return fmt.Errorf("get wmexec version: %w", err)
|
||||
}
|
||||
|
||||
appVersion = c.GetAppVersion()
|
||||
if appVersion != binVersion {
|
||||
return fmt.Errorf("wmexec version mismatch")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM golang:1.21-bookworm as builder
|
||||
FROM golang:1.22-bookworm as builder
|
||||
|
||||
ADD . /root/wingmate
|
||||
WORKDIR /root/wingmate/
|
||||
|
@ -13,8 +13,8 @@ RUN ln -sf /usr/share/zoneinfo/Australia/Sydney /etc/localtime && \
|
|||
apt update && apt install -y procps && \
|
||||
useradd -m -s /bin/bash user1
|
||||
COPY --from=builder /usr/local/bin/wingmate/ /usr/local/bin/
|
||||
ADD --chmod=755 docker/bookworm/entry.sh /usr/local/bin/entry.sh
|
||||
ADD --chmod=755 docker/bookworm/etc /etc
|
||||
ADD --chmod=755 docker/bookworm-newconfig/entry.sh /usr/local/bin/entry.sh
|
||||
ADD --chmod=755 docker/bookworm-newconfig/etc /etc
|
||||
|
||||
ENTRYPOINT [ "/usr/local/bin/entry.sh" ]
|
||||
CMD [ "/usr/local/bin/wingmate" ]
|
|
@ -1,5 +1,8 @@
|
|||
service:
|
||||
one:
|
||||
command: [ "/workspace/wingmate/cmd/experiment/starter/starter" ]
|
||||
environ: [ "DUMMY_PATH=/workspace/wingmate/cmd/experiment/dummy/dummy" ]
|
||||
|
||||
# one:
|
||||
# command: [ "wmstarter" ]
|
||||
# environ: [ "DUMMY_PATH=/workspace/wingmate/cmd/experiment/dummy/dummy" ]
|
||||
|
||||
spawner:
|
||||
command: [ "wmspawner" ]
|
||||
user: "1200"
|
||||
|
|
|
@ -29,6 +29,10 @@ cron:
|
|||
for {
|
||||
if cron.TimeToRun(time.Now()) {
|
||||
wingmate.Log().Info().Str(cronTag, cron.Name()).Msg("executing")
|
||||
if err = cron.UtilDepCheck(); err != nil {
|
||||
wingmate.Log().Error().Str(cronTag, cron.Name()).Msgf("%+v", err)
|
||||
goto fail
|
||||
}
|
||||
cmd = exec.Command(cron.Command(), cron.Arguments()...)
|
||||
iwg = &sync.WaitGroup{}
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ type Task interface {
|
|||
UserGroup() UserGroup
|
||||
WorkingDir() string
|
||||
Status() TaskStatus
|
||||
UtilDepCheck() error
|
||||
}
|
||||
|
||||
type CronTask interface {
|
||||
|
|
|
@ -33,6 +33,11 @@ func (i *Init) service(wg *sync.WaitGroup, task ServiceTask, exitFlag <-chan any
|
|||
service:
|
||||
for {
|
||||
failStatus = false
|
||||
if err = task.UtilDepCheck(); err != nil {
|
||||
wingmate.Log().Error().Str(serviceTag, task.Name()).Msgf("%+v", err)
|
||||
failStatus = true
|
||||
goto fail
|
||||
}
|
||||
cmd = exec.Command(task.Command(), task.Arguments()...)
|
||||
iwg = &sync.WaitGroup{}
|
||||
|
||||
|
|
116
task/cron.go
116
task/cron.go
|
@ -1,6 +1,10 @@
|
|||
package task
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"gitea.suyono.dev/suyono/wingmate"
|
||||
"time"
|
||||
|
||||
wminit "gitea.suyono.dev/suyono/wingmate/init"
|
||||
|
@ -70,14 +74,16 @@ func (cms *CronMultiOccurrenceSpec) Match(u uint8) bool {
|
|||
type CronTask struct {
|
||||
CronSchedule
|
||||
userGroup
|
||||
name string
|
||||
command []string
|
||||
environ []string
|
||||
setsid bool
|
||||
workingDir string
|
||||
lastRun time.Time
|
||||
hasRun bool //NOTE: make sure initialised as false
|
||||
config config
|
||||
cronScheduleString string
|
||||
name string
|
||||
command []string
|
||||
cmdLine []string
|
||||
environ []string
|
||||
setsid bool
|
||||
workingDir string
|
||||
lastRun time.Time
|
||||
hasRun bool //NOTE: make sure initialised as false
|
||||
config config
|
||||
}
|
||||
|
||||
func NewCronTask(name string) *CronTask {
|
||||
|
@ -119,7 +125,8 @@ func (c *CronTask) SetGroup(group string) *CronTask {
|
|||
return c
|
||||
}
|
||||
|
||||
func (c *CronTask) SetSchedule(schedule CronSchedule) *CronTask {
|
||||
func (c *CronTask) SetSchedule(scheduleStr string, schedule CronSchedule) *CronTask {
|
||||
c.cronScheduleString = scheduleStr
|
||||
c.CronSchedule = schedule
|
||||
return c
|
||||
}
|
||||
|
@ -129,21 +136,104 @@ func (c *CronTask) SetConfig(config config) *CronTask {
|
|||
return c
|
||||
}
|
||||
|
||||
func (c *CronTask) Equals(another *CronTask) bool {
|
||||
if another == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
type toCompare struct {
|
||||
Name string
|
||||
Command string
|
||||
Arguments []string
|
||||
Environ []string
|
||||
Setsid bool
|
||||
UserGroup string
|
||||
WorkingDir string
|
||||
Schedule string
|
||||
}
|
||||
|
||||
cmpStruct := func(p *CronTask) ([]byte, error) {
|
||||
s := &toCompare{
|
||||
Name: c.Name(),
|
||||
Command: c.Command(),
|
||||
Arguments: c.Arguments(),
|
||||
Environ: c.Environ(),
|
||||
Setsid: c.Setsid(),
|
||||
UserGroup: c.UserGroup().String(),
|
||||
WorkingDir: c.WorkingDir(),
|
||||
Schedule: c.cronScheduleString,
|
||||
}
|
||||
|
||||
return json.Marshal(s)
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
ours, theirs []byte
|
||||
ourHash, theirHash [sha256.Size]byte
|
||||
)
|
||||
|
||||
if ours, err = cmpStruct(c); err != nil {
|
||||
wingmate.Log().Error().Msgf("cron task equals: %+v", err)
|
||||
return false
|
||||
}
|
||||
ourHash = sha256.Sum256(ours)
|
||||
|
||||
if theirs, err = cmpStruct(another); err != nil {
|
||||
wingmate.Log().Error().Msgf("cron task equals: %+v", err)
|
||||
return false
|
||||
}
|
||||
theirHash = sha256.Sum256(theirs)
|
||||
|
||||
for i := 0; i < sha256.Size; i++ {
|
||||
if ourHash[i] != theirHash[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *CronTask) Name() string {
|
||||
return c.name
|
||||
}
|
||||
|
||||
func (c *CronTask) UtilDepCheck() error {
|
||||
c.cmdLine = make([]string, 0)
|
||||
if c.setsid || c.UserGroup().IsSet() {
|
||||
if err := c.config.WMExecCheckVersion(); err != nil {
|
||||
return fmt.Errorf("utility dependency check: %w", err)
|
||||
}
|
||||
|
||||
c.cmdLine = append(c.cmdLine, c.config.WMExecPath())
|
||||
|
||||
if c.setsid {
|
||||
c.cmdLine = append(c.cmdLine, "--setsid")
|
||||
}
|
||||
|
||||
if c.UserGroup().IsSet() {
|
||||
c.cmdLine = append(c.cmdLine, "--user", c.UserGroup().String())
|
||||
}
|
||||
|
||||
c.cmdLine = append(c.cmdLine, "--")
|
||||
}
|
||||
|
||||
c.cmdLine = append(c.cmdLine, c.command...)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CronTask) Command() string {
|
||||
return c.command[0]
|
||||
return c.cmdLine[0]
|
||||
}
|
||||
|
||||
func (c *CronTask) Arguments() []string {
|
||||
if len(c.command) == 1 {
|
||||
if len(c.cmdLine) == 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
retval := make([]string, len(c.command)-1)
|
||||
copy(retval, c.command[1:])
|
||||
retval := make([]string, len(c.cmdLine)-1)
|
||||
copy(retval, c.cmdLine[1:])
|
||||
|
||||
return retval
|
||||
}
|
||||
|
|
114
task/task.go
114
task/task.go
|
@ -1,14 +1,19 @@
|
|||
package task
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"gitea.suyono.dev/suyono/wingmate"
|
||||
|
||||
wminit "gitea.suyono.dev/suyono/wingmate/init"
|
||||
)
|
||||
|
||||
type config interface {
|
||||
WMPidProxyPath() string
|
||||
WMPidProxyCheckVersion() error
|
||||
WMExecPath() string
|
||||
WMExecCheckVersion() error
|
||||
}
|
||||
|
||||
type Tasks struct {
|
||||
|
@ -33,7 +38,7 @@ func (ts *Tasks) AddService(serviceTask *ServiceTask) *ServiceTask {
|
|||
}
|
||||
|
||||
func (ts *Tasks) AddV0Cron(schedule CronSchedule, path string) {
|
||||
ts.AddCron(NewCronTask(path)).SetCommand(path).SetSchedule(schedule)
|
||||
ts.AddCron(NewCronTask(path)).SetCommand(path).SetSchedule("", schedule)
|
||||
}
|
||||
|
||||
func (ts *Tasks) AddCron(cronTask *CronTask) *CronTask {
|
||||
|
@ -138,6 +143,70 @@ func (t *ServiceTask) SetConfig(config config) *ServiceTask {
|
|||
return t
|
||||
}
|
||||
|
||||
func (t *ServiceTask) Equals(another *ServiceTask) bool {
|
||||
if another == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
type toCompare struct {
|
||||
Name string
|
||||
Command string
|
||||
Arguments []string
|
||||
Environ []string
|
||||
Setsid bool
|
||||
UserGroup string
|
||||
WorkingDir string
|
||||
PidFile string
|
||||
StartSecs uint
|
||||
AutoStart bool
|
||||
AutoRestart bool
|
||||
}
|
||||
|
||||
cmpStruct := func(p *ServiceTask) ([]byte, error) {
|
||||
s := &toCompare{
|
||||
Name: p.Name(),
|
||||
Command: p.Command(),
|
||||
Arguments: p.Arguments(),
|
||||
Environ: p.Environ(),
|
||||
Setsid: p.Setsid(),
|
||||
UserGroup: p.UserGroup().String(),
|
||||
WorkingDir: p.WorkingDir(),
|
||||
PidFile: p.PidFile(),
|
||||
StartSecs: p.StartSecs(),
|
||||
AutoStart: p.AutoStart(),
|
||||
AutoRestart: p.AutoRestart(),
|
||||
}
|
||||
|
||||
return json.Marshal(s)
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
ours, theirs []byte
|
||||
ourHash, theirHash [sha256.Size]byte
|
||||
)
|
||||
|
||||
if ours, err = cmpStruct(t); err != nil {
|
||||
wingmate.Log().Error().Msgf("task equals: %+v", err)
|
||||
return false
|
||||
}
|
||||
ourHash = sha256.Sum256(ours)
|
||||
|
||||
if theirs, err = cmpStruct(another); err != nil {
|
||||
wingmate.Log().Error().Msgf("task equals: %+v", err)
|
||||
return false
|
||||
}
|
||||
theirHash = sha256.Sum256(theirs)
|
||||
|
||||
for i := 0; i < sha256.Size; i++ {
|
||||
if ourHash[i] != theirHash[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *ServiceTask) Validate() error {
|
||||
// call this function for validate the field
|
||||
return validate( /* input the validators here */ )
|
||||
|
@ -176,19 +245,50 @@ func (t *ServiceTask) prepareCommandLine() []string {
|
|||
return t.cmdLine
|
||||
}
|
||||
|
||||
func (t *ServiceTask) UtilDepCheck() error {
|
||||
t.cmdLine = make([]string, 0)
|
||||
if t.background {
|
||||
if err := t.config.WMPidProxyCheckVersion(); err != nil {
|
||||
return fmt.Errorf("utility dependency check: %w", err)
|
||||
}
|
||||
|
||||
t.cmdLine = append(t.cmdLine, t.config.WMPidProxyPath(), "--pid-file", t.pidFile, "--")
|
||||
}
|
||||
|
||||
if t.setsid || t.UserGroup().IsSet() {
|
||||
if err := t.config.WMExecCheckVersion(); err != nil {
|
||||
return fmt.Errorf("utility dependency check: %w", err)
|
||||
}
|
||||
|
||||
t.cmdLine = append(t.cmdLine, t.config.WMExecPath())
|
||||
|
||||
if t.setsid {
|
||||
t.cmdLine = append(t.cmdLine, "--setsid")
|
||||
}
|
||||
|
||||
if t.UserGroup().IsSet() {
|
||||
t.cmdLine = append(t.cmdLine, "--user", t.UserGroup().String())
|
||||
}
|
||||
|
||||
t.cmdLine = append(t.cmdLine, "--")
|
||||
}
|
||||
|
||||
t.cmdLine = append(t.cmdLine, t.command...)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *ServiceTask) Command() string {
|
||||
cl := t.prepareCommandLine()
|
||||
return cl[0]
|
||||
return t.cmdLine[0]
|
||||
}
|
||||
|
||||
func (t *ServiceTask) Arguments() []string {
|
||||
cl := t.prepareCommandLine()
|
||||
if len(cl) == 1 {
|
||||
if len(t.cmdLine) == 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
retval := make([]string, len(cl)-1)
|
||||
copy(retval, cl[1:])
|
||||
retval := make([]string, len(t.cmdLine)-1)
|
||||
copy(retval, t.cmdLine[1:])
|
||||
|
||||
return retval
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue