Compare commits
25 Commits
dd66cb9f1e
...
v0.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
| b2668287ae | |||
| 08a040ac72 | |||
| 6df8ba2810 | |||
| 7785b3ec48 | |||
| 70a4d132c3 | |||
| b15066b513 | |||
| e2275ef05e | |||
| 541228bf68 | |||
| a87c568335 | |||
| 574d3deb56 | |||
| 5679faecdb | |||
| 2a4629c017 | |||
| c043c91f0e | |||
| 15a804aa7d | |||
| 653b4ff158 | |||
| 8cf92167df | |||
| 9c74296c27 | |||
| 8a85ad5107 | |||
| ad8499daa5 | |||
| 8704f80d4b | |||
| dbe9dbba9c | |||
| 4ec5750cd5 | |||
| 3dc69325c1 | |||
| 2eae19f64c | |||
| b589fb8f0c |
@@ -17,7 +17,9 @@
|
|||||||
"vscode": {
|
"vscode": {
|
||||||
"extensions": [
|
"extensions": [
|
||||||
"golang.go",
|
"golang.go",
|
||||||
"ms-azuretools.vscode-docker"
|
"ms-azuretools.vscode-docker",
|
||||||
|
"ms-vscode.makefile-tools",
|
||||||
|
"ms-vscode.cpptools-extension-pack"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1 +1,3 @@
|
|||||||
/cmd/wingmate/wingmate
|
/cmd/wingmate/wingmate
|
||||||
|
/cmd/pidproxy/pidproxy
|
||||||
|
/cmd/exec/exec
|
||||||
54
Makefile
Normal file
54
Makefile
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
DESTDIR = /usr/local/bin
|
||||||
|
|
||||||
|
installs = install-dir
|
||||||
|
programs = wingmate pidproxy exec
|
||||||
|
ifdef TEST_BUILD
|
||||||
|
programs += oneshot spawner starter dummy
|
||||||
|
installs += install-test
|
||||||
|
endif
|
||||||
|
|
||||||
|
all: ${programs}
|
||||||
|
|
||||||
|
wingmate:
|
||||||
|
$(MAKE) -C cmd/wingmate all
|
||||||
|
|
||||||
|
pidproxy:
|
||||||
|
$(MAKE) -C cmd/pidproxy all
|
||||||
|
|
||||||
|
exec:
|
||||||
|
$(MAKE) -C cmd/exec 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/exec 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
|
||||||
|
|
||||||
|
install: ${installs}
|
||||||
|
$(MAKE) -C cmd/wingmate DESTDIR=${DESTDIR} install
|
||||||
|
$(MAKE) -C cmd/pidproxy DESTDIR=${DESTDIR} install
|
||||||
|
$(MAKE) -C cmd/exec DESTDIR=${DESTDIR} install
|
||||||
|
|
||||||
|
install-test:
|
||||||
|
$(MAKE) -C cmd/experiment/dummy DESTDIR=${DESTDIR} install
|
||||||
|
$(MAKE) -C cmd/experiment/oneshot DESTDIR=${DESTDIR} install
|
||||||
|
$(MAKE) -C cmd/experiment/spawner DESTDIR=${DESTDIR} install
|
||||||
|
$(MAKE) -C cmd/experiment/starter DESTDIR=${DESTDIR} install
|
||||||
|
|
||||||
|
install-dir:
|
||||||
|
install -d ${DESTDIR}
|
||||||
137
README.md
137
README.md
@@ -1,2 +1,139 @@
|
|||||||
# wingmate
|
# wingmate
|
||||||
|
|
||||||
|
Wingmate is a process manager for services. It works like init. It starts and restarts services.
|
||||||
|
It also has cron feature. It is designed to run in a container/docker.
|
||||||
|
The Wingmate binary do not need any external dependency.
|
||||||
|
Just copy the binary, and exec from the entry point script.
|
||||||
|
|
||||||
|
# Getting Started
|
||||||
|
|
||||||
|
## Binaries
|
||||||
|
|
||||||
|
There are three binaries in this project: `wingmate`, `wmpidproxy`, and `wmexec`.
|
||||||
|
|
||||||
|
`wingmate` is the core binary. It reads config, starts, restarts services. It also
|
||||||
|
runs cron. Read the [configuration](#configuration) section for files needed to run
|
||||||
|
`wingmate`.
|
||||||
|
|
||||||
|
`wmpidproxy` is a helper binary for monitoring _legacy style_ service (fork, exit
|
||||||
|
initial proces, and continue in background). Read [here](#wingmate-pid-proxy-binary)
|
||||||
|
for further details about `wmpidproxy`.
|
||||||
|
|
||||||
|
`wmexec` is a helper binary for running process in different `user` or `group`.
|
||||||
|
It also useful for setting the process as process group leader.
|
||||||
|
Read [here](#wingmate-exec-binary) for further details about `wmexec`.
|
||||||
|
|
||||||
|
## Building a container image based on wingmate image in Docker Hub
|
||||||
|
|
||||||
|
Wingmate has no dependency other than `alpine` base image, so you just need to copy
|
||||||
|
the binaries directly. If you have built your application into an `alpine` based image,
|
||||||
|
all you need to do is copy whichever binary you need, crontab file (if you use cron)
|
||||||
|
and add some shell script to glue them together. Here is a Dockerfile example.
|
||||||
|
|
||||||
|
```Dockerfile
|
||||||
|
# Dockerfile
|
||||||
|
FROM suyono/wingmate:alpine as source
|
||||||
|
|
||||||
|
FROM alpine:latest
|
||||||
|
ADD --chmod=755 wingmate/ /etc/wingmate/
|
||||||
|
ADD --chmod=755 entry.sh /usr/local/bin/entry.sh
|
||||||
|
COPY --from=source /usr/local/bin/wingmate /usr/local/bin/wingmate
|
||||||
|
COPY --from=source /usr/local/bin/wmpidproxy /usr/local/bin/wmpidproxy
|
||||||
|
COPY --from=source /usr/local/bin/wmexec /usr/local/bin/wmexec
|
||||||
|
ENTRYPOINT [ "/usr/local/bin/entry.sh" ]
|
||||||
|
CMD [ "/usr/local/bin/wingmate" ]
|
||||||
|
```
|
||||||
|
You can find some examples for shell script in [alpine docker](docker/alpine) and
|
||||||
|
[bookworm docker](docker/bookworm).
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
When `wingmate` binary starts, it will look for some files. By default, it will
|
||||||
|
try to read the content of `/etc/wingmate` directory. You can change the directory
|
||||||
|
where it reads by setting `WINGMATE_CONFIG_PATH` environment variable. The structure
|
||||||
|
inside the config path should look like this.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
/etc
|
||||||
|
└── wingmate
|
||||||
|
├── crontab
|
||||||
|
├── crontab.d
|
||||||
|
│ ├── cron1.sh
|
||||||
|
│ ├── cron2.sh
|
||||||
|
│ └── cron3.sh
|
||||||
|
└── service
|
||||||
|
├── one.sh
|
||||||
|
└── spawner.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
First, `wingmate` will try to read the content of `service` directory. The content of
|
||||||
|
this directory should be executables (either shell scripts or binaries). The wingmate
|
||||||
|
will run every executable in `service` directory without going into any subdirectory.
|
||||||
|
|
||||||
|
Next, `wingmate` will read the `crontab` file. `wingmate` expects the `crontab` file using
|
||||||
|
common UNIX crontab file format. Something like this
|
||||||
|
|
||||||
|
```shell
|
||||||
|
┌───────────── minute (0–59)
|
||||||
|
│ ┌───────────── hour (0–23)
|
||||||
|
│ │ ┌───────────── day of the month (1–31)
|
||||||
|
│ │ │ ┌───────────── month (1–12)
|
||||||
|
│ │ │ │ ┌───────────── day of the week (0–6) (Sunday to Saturday)
|
||||||
|
│ │ │ │ │
|
||||||
|
│ │ │ │ │
|
||||||
|
│ │ │ │ │
|
||||||
|
* * * * * <commad or shell script or binary>
|
||||||
|
```
|
||||||
|
|
||||||
|
The command part only support simple command and arguments. Shell expression is not supported
|
||||||
|
yet. It is recommended to write a shell script and put the path to shell script in
|
||||||
|
the command part.
|
||||||
|
|
||||||
|
# Appendix
|
||||||
|
## Wingmate PID Proxy binary
|
||||||
|
|
||||||
|
`wingmate` works by monitoring its direct children process. When it sees one of its children
|
||||||
|
exited, it will start the child process again.
|
||||||
|
|
||||||
|
Sometimes you find some services work by running in the background. It means it forks a new
|
||||||
|
process, disconnect the new child from terminal, exit the parent process, and continue
|
||||||
|
running in the child process. This kind of service usually write its background process
|
||||||
|
PID in a pid file.
|
||||||
|
|
||||||
|
To monitor the background services, `wingmate` utilizes `wmpidproxy`. `wmpidproxy` runs
|
||||||
|
in foreground in-place of the background service. It also periodically check whether the
|
||||||
|
background service is still running, in current implementation it checks every second.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
wmpidproxy --pid-file <path to pid file> -- <background service binary/start script>
|
||||||
|
```
|
||||||
|
#### Example
|
||||||
|
Running sshd background with `wingmate` and `wmpidproxy`: [here](example/ssh-docker)
|
||||||
|
|
||||||
|
#### Note
|
||||||
|
|
||||||
|
It is recommended to configure services to run in foreground if they support it. When services
|
||||||
|
running in foreground, they are running as direct children process of `wingmate`.
|
||||||
|
`wingmate` monitors children process effectively. Whenever a child process exited/terminated,
|
||||||
|
`wingmate` will start it again quickly. Running in foreground also removes the overhead of running
|
||||||
|
`wmpidproxy` together with the service.
|
||||||
|
|
||||||
|
## Wingmate Exec binary
|
||||||
|
|
||||||
|
`wingmate` runs all the services as its children using the same `uid`, `gid`, and in the
|
||||||
|
same process group. You can use `wmexec` to run service in different `uid`, `gid`, or make
|
||||||
|
the service process as its own process group leader.
|
||||||
|
|
||||||
|
#### Syntax
|
||||||
|
|
||||||
|
```shell
|
||||||
|
wmexec [--user <uid>[:<gid>]] [--setsid] -- <target executable>
|
||||||
|
```
|
||||||
|
| Option | Parameter | Description |
|
||||||
|
|----------|-----------|----------------------------------------------------------------------------------------------------------|
|
||||||
|
| --user | uid[:gid] | Set the real user ID and the real group id. Uid and Gid can be either in numeric form or in name form |
|
||||||
|
| --setsid | | Set the process become the leader of its own process group, effectively detaching from parent's terminal |
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
You can find example for `wmexec` in [here](docker/alpine/etc/wingmate) and [here](docker/bookworm/etc/wingmate)
|
||||||
8
cmd/exec/Makefile
Normal file
8
cmd/exec/Makefile
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
all:
|
||||||
|
go build -v
|
||||||
|
|
||||||
|
clean:
|
||||||
|
go clean -i -cache -testcache
|
||||||
|
|
||||||
|
install:
|
||||||
|
install exec ${DESTDIR}/wmexec
|
||||||
11
cmd/exec/ent.go
Normal file
11
cmd/exec/ent.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
//go:build !(cgo && linux)
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
func getUid(user string) (uint64, error) {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGid(group string) (uint64, error) {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
||||||
51
cmd/exec/ent_lin.go
Normal file
51
cmd/exec/ent_lin.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
//go:build cgo && linux
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include<errno.h>
|
||||||
|
#include<string.h>
|
||||||
|
#include<sys/types.h>
|
||||||
|
#include<pwd.h>
|
||||||
|
#include<grp.h>
|
||||||
|
|
||||||
|
static uid_t getuid(const char* username) {
|
||||||
|
struct passwd local, *rv;
|
||||||
|
errno = 0;
|
||||||
|
rv = getpwnam(username);
|
||||||
|
if (errno != 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&local, rv, sizeof(struct passwd));
|
||||||
|
return local.pw_uid;
|
||||||
|
}
|
||||||
|
static gid_t getgid(const char* groupname) {
|
||||||
|
struct group local, *rv;
|
||||||
|
errno = 0;
|
||||||
|
rv = getgrnam(groupname);
|
||||||
|
if (errno != 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&local, rv, sizeof(struct group));
|
||||||
|
return local.gr_gid;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
func getUid(user string) (uint64, error) {
|
||||||
|
u, err := C.getuid(C.CString(user))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return uint64(u), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGid(group string) (uint64, error) {
|
||||||
|
g, err := C.getgid(C.CString(group))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return uint64(g), nil
|
||||||
|
}
|
||||||
133
cmd/exec/exec.go
Normal file
133
cmd/exec/exec.go
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"gitea.suyono.dev/suyono/wingmate"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
setsidFlag = "setsid"
|
||||||
|
EnvSetsid = "SETSID"
|
||||||
|
userFlag = "user"
|
||||||
|
EnvUser = "USER"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
rootCmd = &cobra.Command{
|
||||||
|
Use: "wmexec",
|
||||||
|
RunE: execCmd,
|
||||||
|
}
|
||||||
|
|
||||||
|
childArgs []string
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var (
|
||||||
|
found bool
|
||||||
|
i int
|
||||||
|
arg string
|
||||||
|
selfArgs []string
|
||||||
|
)
|
||||||
|
|
||||||
|
rootCmd.PersistentFlags().BoolP(setsidFlag, "s", false, "set to true to run setsid() before exec")
|
||||||
|
viper.BindPFlag(EnvSetsid, rootCmd.PersistentFlags().Lookup(setsidFlag))
|
||||||
|
|
||||||
|
rootCmd.PersistentFlags().StringP(userFlag, "u", "", "\"user:[group]\"")
|
||||||
|
viper.BindPFlag(EnvUser, rootCmd.PersistentFlags().Lookup(userFlag))
|
||||||
|
|
||||||
|
viper.SetEnvPrefix(wingmate.EnvPrefix)
|
||||||
|
viper.BindEnv(EnvUser)
|
||||||
|
viper.BindEnv(EnvSetsid)
|
||||||
|
viper.SetDefault(EnvSetsid, false)
|
||||||
|
viper.SetDefault(EnvUser, "")
|
||||||
|
|
||||||
|
found = false
|
||||||
|
for i, arg = range os.Args {
|
||||||
|
if arg == "--" {
|
||||||
|
found = true
|
||||||
|
if len(os.Args) <= i+1 {
|
||||||
|
log.Println("invalid argument")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
selfArgs = os.Args[1:i]
|
||||||
|
childArgs = os.Args[i+1:]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
log.Println("invalid argument")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(childArgs) == 0 {
|
||||||
|
log.Println("invalid argument")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
rootCmd.SetArgs(selfArgs)
|
||||||
|
if err := rootCmd.Execute(); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func execCmd(cmd *cobra.Command, args []string) error {
|
||||||
|
if viper.GetBool(EnvSetsid) {
|
||||||
|
_, _ = unix.Setsid()
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
uid uint64
|
||||||
|
gid uint64
|
||||||
|
err error
|
||||||
|
path string
|
||||||
|
)
|
||||||
|
|
||||||
|
ug := viper.GetString(EnvUser)
|
||||||
|
if len(ug) > 0 {
|
||||||
|
user, group, ok := strings.Cut(ug, ":")
|
||||||
|
if ok {
|
||||||
|
if gid, err = strconv.ParseUint(group, 10, 32); err != nil {
|
||||||
|
if gid, err = getGid(group); err != nil {
|
||||||
|
return fmt.Errorf("cgo getgid: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err = unix.Setgid(int(gid)); err != nil {
|
||||||
|
return fmt.Errorf("setgid: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uid, err = strconv.ParseUint(user, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
if uid, err = getUid(user); err != nil {
|
||||||
|
return fmt.Errorf("cgo getuid: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err = unix.Setuid(int(uid)); err != nil {
|
||||||
|
return fmt.Errorf("setuid: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if path, err = exec.LookPath(childArgs[0]); err != nil {
|
||||||
|
if !errors.Is(err, exec.ErrDot) {
|
||||||
|
return fmt.Errorf("lookpath: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = unix.Exec(path, childArgs, os.Environ()); err != nil {
|
||||||
|
return fmt.Errorf("exec: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
8
cmd/experiment/dummy/Makefile
Normal file
8
cmd/experiment/dummy/Makefile
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
all:
|
||||||
|
go build -v
|
||||||
|
|
||||||
|
clean:
|
||||||
|
go clean -i -cache -testcache
|
||||||
|
|
||||||
|
install:
|
||||||
|
install dummy ${DESTDIR}/wmdummy
|
||||||
@@ -7,11 +7,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
for i := 0; i < 10; i++ {
|
log.Println("using log.Println")
|
||||||
log.Println("using log.Println")
|
fmt.Println("using fmt.Println")
|
||||||
fmt.Println("using fmt.Println")
|
|
||||||
time.Sleep(time.Millisecond * 500)
|
time.Sleep(time.Second * 5)
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("log: finishing up")
|
log.Println("log: finishing up")
|
||||||
fmt.Println("fmt: finishing up")
|
fmt.Println("fmt: finishing up")
|
||||||
|
|||||||
8
cmd/experiment/oneshot/Makefile
Normal file
8
cmd/experiment/oneshot/Makefile
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
all:
|
||||||
|
go build -v
|
||||||
|
|
||||||
|
clean:
|
||||||
|
go clean -i -cache -testcache
|
||||||
|
|
||||||
|
install:
|
||||||
|
install oneshot ${DESTDIR}/wmoneshot
|
||||||
@@ -14,22 +14,28 @@ const (
|
|||||||
// DummyPath = "/workspaces/wingmate/cmd/experiment/dummy/dummy"
|
// DummyPath = "/workspaces/wingmate/cmd/experiment/dummy/dummy"
|
||||||
DummyPath = "/usr/local/bin/wmdummy"
|
DummyPath = "/usr/local/bin/wmdummy"
|
||||||
EnvDummyPath = "DUMMY_PATH"
|
EnvDummyPath = "DUMMY_PATH"
|
||||||
EnvPrefix = "WINGMATE"
|
|
||||||
EnvLog = "LOG"
|
EnvLog = "LOG"
|
||||||
EnvLogMessage = "LOG_MESSAGE"
|
EnvLogMessage = "LOG_MESSAGE"
|
||||||
EnvDefaultLogMessage = "oneshot executed"
|
EnvDefaultLogMessage = "oneshot executed"
|
||||||
|
EnvInstanceNum = "INSTANCE_NUM"
|
||||||
|
EnvDefaultInstances = -1
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
viper.SetEnvPrefix(EnvPrefix)
|
viper.SetEnvPrefix(wingmate.EnvPrefix)
|
||||||
viper.BindEnv(EnvDummyPath, EnvLog, EnvLogMessage)
|
viper.BindEnv(EnvDummyPath)
|
||||||
|
viper.BindEnv(EnvLog)
|
||||||
|
viper.BindEnv(EnvLogMessage)
|
||||||
|
viper.BindEnv(EnvInstanceNum)
|
||||||
viper.SetDefault(EnvDummyPath, DummyPath)
|
viper.SetDefault(EnvDummyPath, DummyPath)
|
||||||
viper.SetDefault(EnvLogMessage, EnvDefaultLogMessage)
|
viper.SetDefault(EnvLogMessage, EnvDefaultLogMessage)
|
||||||
|
viper.SetDefault(EnvInstanceNum, EnvDefaultInstances)
|
||||||
|
|
||||||
exePath := viper.GetString(EnvDummyPath)
|
exePath := viper.GetString(EnvDummyPath)
|
||||||
|
|
||||||
logPath := viper.GetString(EnvLog)
|
logPath := viper.GetString(EnvLog)
|
||||||
logMessage := viper.GetString(EnvLogMessage)
|
logMessage := viper.GetString(EnvLogMessage)
|
||||||
|
log.Println("log path:", logPath)
|
||||||
if logPath != "" {
|
if logPath != "" {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
@@ -47,12 +53,17 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StartRandomInstances(exePath)
|
StartInstances(exePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func StartRandomInstances(exePath string) {
|
func StartInstances(exePath string) {
|
||||||
num := (rand.Uint32() % 16) + 16
|
num := (rand.Uint32() % 16) + 16
|
||||||
|
|
||||||
|
iNum := viper.GetInt(EnvInstanceNum)
|
||||||
|
if iNum > 0 {
|
||||||
|
num = uint32(iNum)
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ctr uint32
|
ctr uint32
|
||||||
cmd *exec.Cmd
|
cmd *exec.Cmd
|
||||||
|
|||||||
8
cmd/experiment/spawner/Makefile
Normal file
8
cmd/experiment/spawner/Makefile
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
all:
|
||||||
|
go build -v
|
||||||
|
|
||||||
|
clean:
|
||||||
|
go clean -i -cache -testcache
|
||||||
|
|
||||||
|
install:
|
||||||
|
install spawner ${DESTDIR}/wmspawner
|
||||||
@@ -5,11 +5,11 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"gitea.suyono.dev/suyono/wingmate"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
EnvPrefix = "WINGMATE"
|
|
||||||
EnvOneShotPath = "ONESHOT_PATH"
|
EnvOneShotPath = "ONESHOT_PATH"
|
||||||
OneShotPath = "/usr/local/bin/wmoneshot"
|
OneShotPath = "/usr/local/bin/wmoneshot"
|
||||||
)
|
)
|
||||||
@@ -20,7 +20,7 @@ func main() {
|
|||||||
err error
|
err error
|
||||||
t *time.Ticker
|
t *time.Ticker
|
||||||
)
|
)
|
||||||
viper.SetEnvPrefix(EnvPrefix)
|
viper.SetEnvPrefix(wingmate.EnvPrefix)
|
||||||
viper.BindEnv(EnvOneShotPath)
|
viper.BindEnv(EnvOneShotPath)
|
||||||
viper.SetDefault(EnvOneShotPath, OneShotPath)
|
viper.SetDefault(EnvOneShotPath, OneShotPath)
|
||||||
|
|
||||||
|
|||||||
8
cmd/experiment/starter/Makefile
Normal file
8
cmd/experiment/starter/Makefile
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
all:
|
||||||
|
go build -v
|
||||||
|
|
||||||
|
clean:
|
||||||
|
go clean -i -cache -testcache
|
||||||
|
|
||||||
|
install:
|
||||||
|
install starter ${DESTDIR}/wmstarter
|
||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"gitea.suyono.dev/suyono/wingmate"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -14,7 +15,6 @@ const (
|
|||||||
// DummyPath = "/workspaces/wingmate/cmd/experiment/dummy/dummy"
|
// DummyPath = "/workspaces/wingmate/cmd/experiment/dummy/dummy"
|
||||||
DummyPath = "/usr/local/bin/wmdummy"
|
DummyPath = "/usr/local/bin/wmdummy"
|
||||||
EnvDummyPath = "DUMMY_PATH"
|
EnvDummyPath = "DUMMY_PATH"
|
||||||
EnvPrefix = "WINGMATE"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -25,7 +25,7 @@ func main() {
|
|||||||
err error
|
err error
|
||||||
exePath string
|
exePath string
|
||||||
)
|
)
|
||||||
viper.SetEnvPrefix(EnvPrefix)
|
viper.SetEnvPrefix(wingmate.EnvPrefix)
|
||||||
viper.BindEnv(EnvDummyPath)
|
viper.BindEnv(EnvDummyPath)
|
||||||
viper.SetDefault(EnvDummyPath, DummyPath)
|
viper.SetDefault(EnvDummyPath, DummyPath)
|
||||||
|
|
||||||
|
|||||||
8
cmd/pidproxy/Makefile
Normal file
8
cmd/pidproxy/Makefile
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
all:
|
||||||
|
go build -v
|
||||||
|
|
||||||
|
clean:
|
||||||
|
go clean -i -cache -testcache
|
||||||
|
|
||||||
|
install:
|
||||||
|
install pidproxy ${DESTDIR}/wmpidproxy
|
||||||
161
cmd/pidproxy/pidproxy.go
Normal file
161
cmd/pidproxy/pidproxy.go
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"errors"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"os/signal"
|
||||||
|
"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)
|
||||||
|
|
||||||
|
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
|
||||||
|
if len(os.Args) <= i+1 {
|
||||||
|
log.Println("invalid argument")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
selfArgs = os.Args[1:i]
|
||||||
|
childArgs = os.Args[i+1:]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
log.Println("invalid argument")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(childArgs) == 0 {
|
||||||
|
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
|
||||||
|
sc chan os.Signal
|
||||||
|
t *time.Ticker
|
||||||
|
)
|
||||||
|
|
||||||
|
sc = make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sc, unix.SIGTERM)
|
||||||
|
|
||||||
|
t = time.NewTicker(time.Second)
|
||||||
|
|
||||||
|
check:
|
||||||
|
for {
|
||||||
|
if pid, err = readPid(pidfile); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = unix.Kill(pid, syscall.Signal(0)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-t.C:
|
||||||
|
case <-sc:
|
||||||
|
if pid, err = readPid(pidfile); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = unix.Kill(pid, unix.SIGTERM); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
break check
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readPid(pidFile string) (int, error) {
|
||||||
|
var (
|
||||||
|
file *os.File
|
||||||
|
err error
|
||||||
|
pid64 int64
|
||||||
|
)
|
||||||
|
|
||||||
|
if file, err = os.Open(pidFile); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = file.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
if scanner.Scan() {
|
||||||
|
if pid64, err = strconv.ParseInt(scanner.Text(), 10, 64); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return int(pid64), nil
|
||||||
|
} else {
|
||||||
|
return 0, errors.New("invalid scanner")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func startProcess(arg0 string, args ...string) {
|
||||||
|
if err := exec.Command(arg0, args...).Run(); err != nil {
|
||||||
|
log.Println("exec:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
9
cmd/wingmate/Makefile
Normal file
9
cmd/wingmate/Makefile
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
all:
|
||||||
|
go build -v
|
||||||
|
|
||||||
|
clean:
|
||||||
|
go clean -i -cache -testcache
|
||||||
|
|
||||||
|
install:
|
||||||
|
install wingmate ${DESTDIR}/wingmate
|
||||||
|
|
||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"gitea.suyono.dev/suyono/wingmate"
|
"gitea.suyono.dev/suyono/wingmate"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -48,8 +49,11 @@ func Read() (*Config, error) {
|
|||||||
if len(dirent) > 0 {
|
if len(dirent) > 0 {
|
||||||
for _, d := range dirent {
|
for _, d := range dirent {
|
||||||
if d.Type().IsRegular() {
|
if d.Type().IsRegular() {
|
||||||
serviceAvailable = true
|
svcPath := filepath.Join(svcdir, d.Name())
|
||||||
outConfig.ServicePaths = append(outConfig.ServicePaths, filepath.Join(svcdir, d.Name()))
|
if err = unix.Access(svcPath, unix.X_OK); err == nil {
|
||||||
|
serviceAvailable = true
|
||||||
|
outConfig.ServicePaths = append(outConfig.ServicePaths, svcPath)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
130
config/config_test.go
Normal file
130
config/config_test.go
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gitea.suyono.dev/suyono/wingmate"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRead(t *testing.T) {
|
||||||
|
|
||||||
|
type testEntry struct {
|
||||||
|
name string
|
||||||
|
testFunc func(t *testing.T)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
configDir string
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
const serviceDir = "service"
|
||||||
|
|
||||||
|
setup := func(t *testing.T) {
|
||||||
|
if configDir, err = os.MkdirTemp("", "wingmate-*-test"); err != nil {
|
||||||
|
t.Fatal("setup", err)
|
||||||
|
}
|
||||||
|
viper.Set(EnvConfigPath, configDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
tear := func(t *testing.T) {
|
||||||
|
if err = os.RemoveAll(configDir); err != nil {
|
||||||
|
t.Fatal("tear", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mkSvcDir := func(t *testing.T) {
|
||||||
|
if err := os.MkdirAll(path.Join(configDir, serviceDir), 0755); err != nil {
|
||||||
|
t.Fatal("create dir", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
touchFile := func(t *testing.T, name string, perm os.FileMode) {
|
||||||
|
f, err := os.OpenFile(name, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, perm)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("create file", err)
|
||||||
|
}
|
||||||
|
_ = f.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = wingmate.NewLog(os.Stderr)
|
||||||
|
tests := []testEntry{
|
||||||
|
{
|
||||||
|
name: "positive",
|
||||||
|
testFunc: func(t *testing.T) {
|
||||||
|
mkSvcDir(t)
|
||||||
|
touchFile(t, path.Join(configDir, serviceDir, "one.sh"), 0755)
|
||||||
|
touchFile(t, path.Join(configDir, serviceDir, "two.sh"), 0755)
|
||||||
|
|
||||||
|
cfg, err := Read()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.ElementsMatch(
|
||||||
|
t,
|
||||||
|
cfg.ServicePaths,
|
||||||
|
[]string{
|
||||||
|
path.Join(configDir, serviceDir, "one.sh"),
|
||||||
|
path.Join(configDir, serviceDir, "two.sh"),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with directory",
|
||||||
|
testFunc: func(t *testing.T) {
|
||||||
|
const subdir1 = "subdir1"
|
||||||
|
mkSvcDir(t)
|
||||||
|
assert.Nil(t, os.Mkdir(path.Join(configDir, serviceDir, subdir1), 0755))
|
||||||
|
touchFile(t, path.Join(configDir, serviceDir, subdir1, "one.sh"), 0755)
|
||||||
|
touchFile(t, path.Join(configDir, serviceDir, "two.sh"), 0755)
|
||||||
|
cfg, err := Read()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.ElementsMatch(
|
||||||
|
t,
|
||||||
|
cfg.ServicePaths,
|
||||||
|
[]string{
|
||||||
|
path.Join(configDir, serviceDir, "two.sh"),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wrong mode",
|
||||||
|
testFunc: func(t *testing.T) {
|
||||||
|
mkSvcDir(t)
|
||||||
|
touchFile(t, path.Join(configDir, serviceDir, "one.sh"), 0755)
|
||||||
|
touchFile(t, path.Join(configDir, serviceDir, "two.sh"), 0644)
|
||||||
|
|
||||||
|
cfg, err := Read()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.ElementsMatch(
|
||||||
|
t,
|
||||||
|
cfg.ServicePaths,
|
||||||
|
[]string{
|
||||||
|
path.Join(configDir, serviceDir, "one.sh"),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty",
|
||||||
|
testFunc: func(t *testing.T) {
|
||||||
|
mkSvcDir(t)
|
||||||
|
|
||||||
|
_, err := Read()
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
setup(t)
|
||||||
|
tt.testFunc(t)
|
||||||
|
tear(t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -126,23 +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())) {
|
||||||
c.lastRun = now
|
|
||||||
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
|
||||||
|
|||||||
@@ -1,31 +1,18 @@
|
|||||||
FROM golang:1.21-alpine as builder
|
FROM golang:1.21-alpine as builder
|
||||||
|
|
||||||
ADD . /root/wingmate
|
ADD . /root/wingmate
|
||||||
WORKDIR /root/wingmate/cmd/wingmate
|
WORKDIR /root/wingmate/
|
||||||
RUN go build -v
|
ARG TEST_BUILD
|
||||||
|
RUN apk add make build-base && CGO_ENABLED=1 make all && make DESTDIR=/usr/local/bin/wingmate install
|
||||||
WORKDIR /root/wingmate/cmd/experiment/dummy
|
|
||||||
RUN go build -v
|
|
||||||
|
|
||||||
WORKDIR /root/wingmate/cmd/experiment/starter
|
|
||||||
RUN go build -v
|
|
||||||
|
|
||||||
WORKDIR /root/wingmate/cmd/experiment/spawner
|
|
||||||
RUN go build -v
|
|
||||||
|
|
||||||
WORKDIR /root/wingmate/cmd/experiment/oneshot
|
|
||||||
RUN go build -v
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
FROM alpine:3.18
|
FROM alpine:3.18
|
||||||
|
|
||||||
RUN apk add tzdata && ln -s /usr/share/zoneinfo/Australia/Sydney /etc/localtime
|
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
|
adduser -h /home/user1 -D -s /bin/sh user1 && \
|
||||||
COPY --from=builder /root/wingmate/cmd/experiment/dummy/dummy /usr/local/bin/wmdummy
|
adduser -h /home/user2 -D -s /bin/sh user2
|
||||||
COPY --from=builder /root/wingmate/cmd/experiment/starter/starter /usr/local/bin/wmstarter
|
COPY --from=builder /usr/local/bin/wingmate/ /usr/local/bin/
|
||||||
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
|
|
||||||
ADD --chmod=755 docker/alpine/entry.sh /usr/local/bin/entry.sh
|
ADD --chmod=755 docker/alpine/entry.sh /usr/local/bin/entry.sh
|
||||||
ADD --chmod=755 docker/alpine/etc /etc
|
ADD --chmod=755 docker/alpine/etc /etc
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1,3 @@
|
|||||||
*/5 * * * * /etc/wingmate/crontab.d/cron1.sh
|
*/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
|
||||||
@@ -3,4 +3,7 @@
|
|||||||
export WINGMATE_DUMMY_PATH=/usr/local/bin/wmdummy
|
export WINGMATE_DUMMY_PATH=/usr/local/bin/wmdummy
|
||||||
export WINGMATE_LOG=/var/log/cron1.log
|
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"
|
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
|
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
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
export DUMMY_PATH=/usr/local/bin/wmdummy
|
export DUMMY_PATH=/usr/local/bin/wmdummy
|
||||||
exec /usr/local/bin/wmstarter
|
exec /usr/local/bin/wmexec --setsid --user user1:user1 -- /usr/local/bin/wmstarter
|
||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
export WINGMATE_ONESHOT_PATH=/usr/local/bin/wmoneshot
|
export WINGMATE_ONESHOT_PATH=/usr/local/bin/wmoneshot
|
||||||
export WINGMATE_DUMMY_PATH=/usr/local/bin/wmdummy
|
export WINGMATE_DUMMY_PATH=/usr/local/bin/wmdummy
|
||||||
exec /usr/local/bin/wmspawner
|
exec /usr/local/bin/wmexec --user 1001 -- /usr/local/bin/wmspawner
|
||||||
20
docker/bookworm/Dockerfile
Normal file
20
docker/bookworm/Dockerfile
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
FROM golang:1.21-bookworm as builder
|
||||||
|
|
||||||
|
ADD . /root/wingmate
|
||||||
|
WORKDIR /root/wingmate/
|
||||||
|
ARG TEST_BUILD
|
||||||
|
RUN make all && make DESTDIR=/usr/local/bin/wingmate install
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
FROM debian:bookworm
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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/wmexec --setsid --user user1:user1 -- /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/wmexec --user 1200 -- /usr/local/bin/wmspawner
|
||||||
14
example/ssh-docker/Dockerfile
Normal file
14
example/ssh-docker/Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
FROM suyono/wingmate:alpine as source
|
||||||
|
|
||||||
|
FROM alpine:3.19
|
||||||
|
|
||||||
|
RUN apk update && apk add tzdata openssh-server && \
|
||||||
|
ln -s /usr/share/zoneinfo/Australia/Sydney /etc/localtime && ssh-keygen -A
|
||||||
|
|
||||||
|
COPY --from=source /usr/local/bin/wingmate /usr/local/bin/
|
||||||
|
COPY --from=source /usr/local/bin/wmpidproxy /usr/local/bin/
|
||||||
|
ADD --chmod=755 example/ssh-docker/entry.sh /usr/local/bin/entry.sh
|
||||||
|
ADD --chmod=755 example/ssh-docker/etc /etc
|
||||||
|
|
||||||
|
ENTRYPOINT [ "/usr/local/bin/entry.sh" ]
|
||||||
|
CMD [ "/usr/local/bin/wingmate" ]
|
||||||
7
example/ssh-docker/entry.sh
Normal file
7
example/ssh-docker/entry.sh
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if [ $# -gt 0 ]; then
|
||||||
|
exec "$@"
|
||||||
|
else
|
||||||
|
exec /usr/local/bin/wingmate
|
||||||
|
fi
|
||||||
3
example/ssh-docker/etc/wingmate/service/sshd.sh
Normal file
3
example/ssh-docker/etc/wingmate/service/sshd.sh
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
exec /usr/local/bin/wmpidproxy --pid-file /var/run/sshd.pid -- /usr/sbin/sshd
|
||||||
7
go.mod
7
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
|
||||||
@@ -28,6 +28,7 @@ require (
|
|||||||
github.com/spf13/cast v1.6.0 // indirect
|
github.com/spf13/cast v1.6.0 // indirect
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
|
github.com/stretchr/objx v0.5.1 // indirect
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
go.uber.org/atomic v1.11.0 // indirect
|
go.uber.org/atomic v1.11.0 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
|
|||||||
10
go.sum
10
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=
|
||||||
@@ -194,12 +201,15 @@ github.com/spf13/viper v1.17.0/go.mod h1:BmMMMLQXSbcHK6KAOiFLz0l5JHrU89OdIRHvsk0
|
|||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0=
|
||||||
|
github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||||
|
|||||||
@@ -23,10 +23,11 @@ func (i *Init) cron(wg *sync.WaitGroup, cron Cron, exitFlag <-chan any) {
|
|||||||
stderr io.ReadCloser
|
stderr io.ReadCloser
|
||||||
)
|
)
|
||||||
|
|
||||||
ticker := time.NewTicker(time.Second * 20)
|
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())
|
||||||
iwg = &sync.WaitGroup{}
|
iwg = &sync.WaitGroup{}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package init
|
package init
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -34,17 +35,19 @@ func (i *Init) Start() {
|
|||||||
wg *sync.WaitGroup
|
wg *sync.WaitGroup
|
||||||
signalTrigger chan any
|
signalTrigger chan any
|
||||||
sighandlerExit chan any
|
sighandlerExit chan any
|
||||||
|
sigchld chan os.Signal
|
||||||
)
|
)
|
||||||
|
|
||||||
signalTrigger = make(chan any)
|
signalTrigger = make(chan any)
|
||||||
sighandlerExit = make(chan any)
|
sighandlerExit = make(chan any)
|
||||||
|
sigchld = make(chan os.Signal, 1)
|
||||||
|
|
||||||
wg = &sync.WaitGroup{}
|
wg = &sync.WaitGroup{}
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go i.waiter(wg, signalTrigger, sighandlerExit)
|
go i.waiter(wg, signalTrigger, sighandlerExit, sigchld)
|
||||||
|
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go i.sighandler(wg, signalTrigger, sighandlerExit)
|
go i.sighandler(wg, signalTrigger, sighandlerExit, sigchld)
|
||||||
|
|
||||||
for _, s := range i.config.Services() {
|
for _, s := range i.config.Services() {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"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, sigchld chan<- os.Signal) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
@@ -35,7 +35,10 @@ signal:
|
|||||||
isOpen = false
|
isOpen = false
|
||||||
}
|
}
|
||||||
case unix.SIGCHLD:
|
case unix.SIGCHLD:
|
||||||
// do nothing
|
select {
|
||||||
|
case sigchld <- s:
|
||||||
|
default:
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case <-selfExit:
|
case <-selfExit:
|
||||||
|
|||||||
@@ -2,20 +2,20 @@ package init
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
"gitea.suyono.dev/suyono/wingmate"
|
"gitea.suyono.dev/suyono/wingmate"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (i *Init) waiter(wg *sync.WaitGroup, runningFlag <-chan any, sigHandlerFlag chan<- any) {
|
func (i *Init) waiter(wg *sync.WaitGroup, runningFlag <-chan any, sigHandlerFlag chan<- any, sigchld <-chan os.Signal) {
|
||||||
var (
|
var (
|
||||||
ws unix.WaitStatus
|
ws unix.WaitStatus
|
||||||
// pid int
|
err error
|
||||||
err error
|
running bool
|
||||||
running bool
|
flagged bool
|
||||||
flagged bool
|
waitingForSignal bool
|
||||||
)
|
)
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
@@ -25,14 +25,27 @@ func (i *Init) waiter(wg *sync.WaitGroup, runningFlag <-chan any, sigHandlerFlag
|
|||||||
|
|
||||||
running = true
|
running = true
|
||||||
flagged = true
|
flagged = true
|
||||||
|
waitingForSignal = true
|
||||||
wait:
|
wait:
|
||||||
for {
|
for {
|
||||||
if running {
|
if running {
|
||||||
select {
|
if waitingForSignal {
|
||||||
case <-runningFlag:
|
select {
|
||||||
wingmate.Log().Info().Msg("waiter received shutdown signal...")
|
case <-runningFlag:
|
||||||
running = false
|
wingmate.Log().Info().Msg("waiter received shutdown signal...")
|
||||||
default:
|
running = false
|
||||||
|
case <-sigchld:
|
||||||
|
waitingForSignal = false
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
select {
|
||||||
|
case <-runningFlag:
|
||||||
|
wingmate.Log().Info().Msg("waiter received shutdown signal...")
|
||||||
|
running = false
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +63,7 @@ wait:
|
|||||||
}
|
}
|
||||||
|
|
||||||
wingmate.Log().Warn().Msgf("Wait4 returns error: %+v", err)
|
wingmate.Log().Warn().Msgf("Wait4 returns error: %+v", err)
|
||||||
time.Sleep(time.Millisecond * 100)
|
waitingForSignal = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user