Compare commits
4 Commits
96f67941ce
...
847c5a2d08
| Author | SHA1 | Date | |
|---|---|---|---|
| 847c5a2d08 | |||
| ea7358a35f | |||
| c51dfce9ec | |||
| 04718759c4 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
bin/
|
bin/
|
||||||
|
config.yaml
|
||||||
70
cmd/reload.go
Normal file
70
cmd/reload.go
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"git.oblat.lv/alex/triggerssmith/internal/vars"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var optsReloadCmd = struct {
|
||||||
|
Debug *bool
|
||||||
|
PID *int
|
||||||
|
}{}
|
||||||
|
|
||||||
|
func readPID(path string) (int, error) {
|
||||||
|
data, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
s := strings.TrimSpace(string(data))
|
||||||
|
|
||||||
|
pid, err := strconv.Atoi(s)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var reloadCmd = &cobra.Command{
|
||||||
|
Use: "reload",
|
||||||
|
Short: "Reload active server by PID using SIGHUP",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
slog.Error("Application panicked", slog.Any("error", r))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
// configure logger
|
||||||
|
if *optsReloadCmd.Debug {
|
||||||
|
slog.SetDefault(slog.New(slog.NewTextHandler(cmd.OutOrStdout(), &slog.HandlerOptions{Level: slog.LevelDebug, AddSource: true})))
|
||||||
|
} else {
|
||||||
|
slog.SetDefault(slog.New(slog.NewTextHandler(cmd.OutOrStdout(), &slog.HandlerOptions{Level: slog.LevelInfo})))
|
||||||
|
}
|
||||||
|
pid, err := readPID(vars.PID_PATH)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
*optsReloadCmd.PID = pid
|
||||||
|
slog.Debug("restarting server", slog.Int("pid", *optsReloadCmd.PID))
|
||||||
|
proc, err := os.FindProcess(*optsReloadCmd.PID)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("failed to find process", slog.Int("pid", *optsReloadCmd.PID), slog.String("err", err.Error()))
|
||||||
|
}
|
||||||
|
proc.Signal(syscall.SIGHUP)
|
||||||
|
slog.Debug("done")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
optsReloadCmd.Debug = reloadCmd.Flags().BoolP("debug", "d", false, "Enable debug logs")
|
||||||
|
optsReloadCmd.PID = reloadCmd.Flags().IntP("pid", "p", -1, "Define server PID")
|
||||||
|
rootCmd.AddCommand(reloadCmd)
|
||||||
|
}
|
||||||
126
cmd/serve.go
126
cmd/serve.go
@@ -5,14 +5,18 @@ import (
|
|||||||
"log/slog"
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
application "git.oblat.lv/alex/triggerssmith/internal/app"
|
application "git.oblat.lv/alex/triggerssmith/internal/app"
|
||||||
"git.oblat.lv/alex/triggerssmith/internal/config"
|
"git.oblat.lv/alex/triggerssmith/internal/config"
|
||||||
|
"git.oblat.lv/alex/triggerssmith/internal/vars"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var opts = struct {
|
var optsServeCmd = struct {
|
||||||
ConfigPath *string
|
ConfigPath *string
|
||||||
Debug *bool
|
Debug *bool
|
||||||
}{}
|
}{}
|
||||||
@@ -38,29 +42,49 @@ func loggingMiddleware(next http.Handler) http.Handler {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func writePID(path string) error {
|
||||||
|
pid := os.Getpid()
|
||||||
|
|
||||||
|
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
_, err = fmt.Fprintf(f, "%d\n", pid)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
var serveCmd = &cobra.Command{
|
var serveCmd = &cobra.Command{
|
||||||
Use: "serve",
|
Use: "serve",
|
||||||
Short: "Start the server",
|
Short: "Start the server",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
// defer func() {
|
defer func() {
|
||||||
// if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
// slog.Error("Application panicked", slog.Any("error", r))
|
slog.Error("Application panicked", slog.Any("error", r))
|
||||||
// }
|
os.Exit(-1)
|
||||||
// }()
|
}
|
||||||
|
}()
|
||||||
// configure logger
|
// configure logger
|
||||||
if *opts.Debug {
|
if *optsServeCmd.Debug {
|
||||||
slog.SetDefault(slog.New(slog.NewTextHandler(cmd.OutOrStdout(), &slog.HandlerOptions{Level: slog.LevelDebug, AddSource: true})))
|
slog.SetDefault(slog.New(slog.NewTextHandler(cmd.OutOrStdout(), &slog.HandlerOptions{Level: slog.LevelDebug, AddSource: true})))
|
||||||
} else {
|
} else {
|
||||||
slog.SetDefault(slog.New(slog.NewTextHandler(cmd.OutOrStdout(), &slog.HandlerOptions{Level: slog.LevelInfo})))
|
slog.SetDefault(slog.New(slog.NewTextHandler(cmd.OutOrStdout(), &slog.HandlerOptions{Level: slog.LevelInfo})))
|
||||||
}
|
}
|
||||||
|
|
||||||
slog.Debug("Starting server")
|
pid := os.Getpid()
|
||||||
|
slog.Debug("Starting server", slog.Int("pid", pid))
|
||||||
|
if err := writePID(vars.PID_PATH); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
slog.Debug("created pid file", slog.String("path", vars.PID_PATH))
|
||||||
|
defer os.Remove(vars.PID_PATH)
|
||||||
|
|
||||||
// load config
|
// load config
|
||||||
slog.Debug("Reading configuration", slog.String("path", *opts.ConfigPath))
|
slog.Debug("Reading configuration", slog.String("path", *optsServeCmd.ConfigPath))
|
||||||
cfg, err := config.LoadConfig(*opts.ConfigPath)
|
cfg, err := config.LoadConfig(*optsServeCmd.ConfigPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("Failed to load configuration", slog.String("path", *opts.ConfigPath), slog.String("error", err.Error()))
|
slog.Error("Failed to load configuration", slog.String("path", *optsServeCmd.ConfigPath), slog.String("error", err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
slog.Debug("Configuration loaded", slog.Any("config", cfg))
|
slog.Debug("Configuration loaded", slog.Any("config", cfg))
|
||||||
@@ -73,39 +97,6 @@ var serveCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
app.LoadConfiguration(cfg)
|
app.LoadConfiguration(cfg)
|
||||||
|
|
||||||
/*
|
|
||||||
// setup handlers
|
|
||||||
mux := http.NewServeMux()
|
|
||||||
|
|
||||||
// static files
|
|
||||||
staticPath := cfg.Server.StaticFilesPath
|
|
||||||
slog.Debug("Setting up static file server", slog.String("path", staticPath))
|
|
||||||
fs := http.FileServer(http.Dir(staticPath))
|
|
||||||
mux.Handle("/static/", http.StripPrefix("/static/", fs))
|
|
||||||
handler := loggingMiddleware(mux)
|
|
||||||
*/
|
|
||||||
|
|
||||||
// start server
|
|
||||||
/*addr := fmt.Sprintf("%s:%d", cfg.Server.Addr, cfg.Server.Port)
|
|
||||||
slog.Debug("Binding listener", slog.String("address", addr))
|
|
||||||
|
|
||||||
ln, err := net.Listen("tcp", addr)
|
|
||||||
if err != nil {
|
|
||||||
slog.Error("Failed to start listener", slog.String("address", addr), slog.String("error", err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
srv := &http.Server{
|
|
||||||
Addr: addr,
|
|
||||||
Handler: handler,
|
|
||||||
}
|
|
||||||
|
|
||||||
slog.Info("Server started", slog.String("address", addr))
|
|
||||||
|
|
||||||
if err := srv.Serve(ln); err != nil && err != http.ErrServerClosed {
|
|
||||||
slog.Error("HTTP server stopped with error", slog.String("error", err.Error()))
|
|
||||||
}*/
|
|
||||||
|
|
||||||
server := app.Server()
|
server := app.Server()
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
|
|
||||||
@@ -129,26 +120,49 @@ var serveCmd = &cobra.Command{
|
|||||||
slog.Info("Server started", slog.String("address", net.JoinHostPort(cfg.Server.Addr, fmt.Sprintf("%d", cfg.Server.Port))))
|
slog.Info("Server started", slog.String("address", net.JoinHostPort(cfg.Server.Addr, fmt.Sprintf("%d", cfg.Server.Port))))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sigch := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigch, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
|
||||||
for true {
|
for true {
|
||||||
fmt.Scanln()
|
sig := <-sigch
|
||||||
if err := config.ReloadConfig(cfg); err != nil {
|
slog.Debug("got signal", slog.Any("os.Signal", sig))
|
||||||
slog.Error("Failed to reload configuration", slog.String("error", err.Error()))
|
switch sig {
|
||||||
} else {
|
case syscall.SIGHUP:
|
||||||
slog.Info("Configuration reloaded", slog.Any("config", cfg))
|
if err := config.ReloadConfig(cfg); err != nil {
|
||||||
var addr = net.JoinHostPort(cfg.Server.Addr, fmt.Sprintf("%d", cfg.Server.Port))
|
slog.Error("Failed to reload configuration", slog.String("error", err.Error()))
|
||||||
err = server.Reload(addr)
|
|
||||||
if err != nil {
|
|
||||||
slog.Error("Failed to restart server with new configuration", slog.String("error", err.Error()))
|
|
||||||
} else {
|
} else {
|
||||||
slog.Info("Server restarted with new configuration", slog.String("address", net.JoinHostPort(cfg.Server.Addr, fmt.Sprintf("%d", cfg.Server.Port))))
|
slog.Info("Configuration reloaded")
|
||||||
|
var addr = net.JoinHostPort(cfg.Server.Addr, fmt.Sprintf("%d", cfg.Server.Port))
|
||||||
|
slog.Debug("New configuration", slog.Any("config", cfg))
|
||||||
|
err = server.Reload(addr)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("Failed to restart server with new configuration", slog.String("error", err.Error()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
case syscall.SIGINT:
|
||||||
|
slog.Info("Stopping server by SIGINT")
|
||||||
|
err := server.Stop()
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("Failed to stop server", slog.String("err", err.Error()))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
os.Remove(vars.PID_PATH)
|
||||||
|
return
|
||||||
|
case syscall.SIGTERM:
|
||||||
|
slog.Info("Stopping server by SIGTERM")
|
||||||
|
err := server.Stop()
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("Failed to stop server", slog.String("err", err.Error()))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
os.Remove(vars.PID_PATH)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
opts.Debug = serveCmd.Flags().BoolP("debug", "d", false, "Enable debug logs")
|
optsServeCmd.Debug = serveCmd.Flags().BoolP("debug", "d", false, "Enable debug logs")
|
||||||
opts.ConfigPath = serveCmd.Flags().StringP("config", "c", "config.yaml", "Path to configuration file")
|
optsServeCmd.ConfigPath = serveCmd.Flags().StringP("config", "c", "config.yaml", "Path to configuration file")
|
||||||
rootCmd.AddCommand(serveCmd)
|
rootCmd.AddCommand(serveCmd)
|
||||||
}
|
}
|
||||||
|
|||||||
51
cmd/stop.go
Normal file
51
cmd/stop.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"git.oblat.lv/alex/triggerssmith/internal/vars"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var optsStopCmd = struct {
|
||||||
|
Debug *bool
|
||||||
|
PID *int
|
||||||
|
}{}
|
||||||
|
|
||||||
|
var stopCmd = &cobra.Command{
|
||||||
|
Use: "stop",
|
||||||
|
Short: "Stop active server by PID using SIGTERM",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
slog.Error("Application panicked", slog.Any("error", r))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
// configure logger
|
||||||
|
if *optsReloadCmd.Debug {
|
||||||
|
slog.SetDefault(slog.New(slog.NewTextHandler(cmd.OutOrStdout(), &slog.HandlerOptions{Level: slog.LevelDebug, AddSource: true})))
|
||||||
|
} else {
|
||||||
|
slog.SetDefault(slog.New(slog.NewTextHandler(cmd.OutOrStdout(), &slog.HandlerOptions{Level: slog.LevelInfo})))
|
||||||
|
}
|
||||||
|
pid, err := readPID(vars.PID_PATH)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
*optsStopCmd.PID = pid
|
||||||
|
slog.Debug("restarting server", slog.Int("pid", *optsStopCmd.PID))
|
||||||
|
proc, err := os.FindProcess(*optsStopCmd.PID)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("failed to find process", slog.Int("pid", *optsStopCmd.PID), slog.String("err", err.Error()))
|
||||||
|
}
|
||||||
|
proc.Signal(syscall.SIGTERM)
|
||||||
|
slog.Debug("done")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
optsStopCmd.Debug = stopCmd.Flags().BoolP("debug", "d", false, "Enable debug logs")
|
||||||
|
optsStopCmd.PID = stopCmd.Flags().IntP("pid", "p", -1, "Define server PID")
|
||||||
|
rootCmd.AddCommand(stopCmd)
|
||||||
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
server:
|
|
||||||
address: "0.0.0.0"
|
|
||||||
port: 8089
|
|
||||||
@@ -94,17 +94,17 @@ type LiveServer struct {
|
|||||||
handler http.Handler
|
handler http.Handler
|
||||||
|
|
||||||
active atomic.Value // *instance
|
active atomic.Value // *instance
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
|
|
||||||
statusMu sync.Mutex
|
statusMu sync.Mutex
|
||||||
status Status
|
status Status
|
||||||
|
|
||||||
initDone bool
|
initDone bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type instance struct {
|
type instance struct {
|
||||||
srv *http.Server
|
srv *http.Server
|
||||||
ln net.Listener
|
ln net.Listener
|
||||||
|
|
||||||
addr string
|
addr string
|
||||||
}
|
}
|
||||||
@@ -198,10 +198,10 @@ func (ls *LiveServer) Start(addr string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
srv := &http.Server{Handler: ls.handler}
|
srv := &http.Server{Handler: ls.handler}
|
||||||
|
|
||||||
ls.active.Store(&instance{
|
ls.active.Store(&instance{
|
||||||
srv: srv,
|
srv: srv,
|
||||||
ln: ln,
|
ln: ln,
|
||||||
addr: addr,
|
addr: addr,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -249,7 +249,6 @@ func (ls *LiveServer) Stop() error {
|
|||||||
return ls.stop(inst)
|
return ls.stop(inst)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (ls *LiveServer) Reload(newAddr string) error {
|
func (ls *LiveServer) Reload(newAddr string) error {
|
||||||
ls.mu.Lock()
|
ls.mu.Lock()
|
||||||
oldInstAny := ls.active.Load()
|
oldInstAny := ls.active.Load()
|
||||||
@@ -258,8 +257,7 @@ func (ls *LiveServer) Reload(newAddr string) error {
|
|||||||
oldAddr = oldInstAny.(*instance).addr
|
oldAddr = oldInstAny.(*instance).addr
|
||||||
}
|
}
|
||||||
ls.mu.Unlock()
|
ls.mu.Unlock()
|
||||||
|
|
||||||
println("Old addr:", oldAddr, "New addr:", newAddr)
|
|
||||||
if oldAddr == newAddr {
|
if oldAddr == newAddr {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -448,7 +446,6 @@ func (ls *LiveServer) Reload(newAddr string) error {
|
|||||||
// // }
|
// // }
|
||||||
|
|
||||||
// ls.setStatus(Status{ID: StatusStarting})
|
// ls.setStatus(Status{ID: StatusStarting})
|
||||||
|
|
||||||
|
|
||||||
// err := ls.Start()
|
// err := ls.Start()
|
||||||
// if err != nil {
|
// if err != nil {
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Status struct {
|
type Status struct {
|
||||||
ID StatusID
|
ID StatusID
|
||||||
Err error
|
Err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Status) Error() string {
|
func (s Status) Error() string {
|
||||||
return s.Err.Error()
|
return s.Err.Error()
|
||||||
}
|
}
|
||||||
|
|||||||
4
internal/vars/const.go
Normal file
4
internal/vars/const.go
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
package vars
|
||||||
|
|
||||||
|
const VAR_PATH = "/var/run/triggerssmith/"
|
||||||
|
const PID_PATH = VAR_PATH + "serve.pid"
|
||||||
Reference in New Issue
Block a user