2020-09-28 13:46:37 +00:00
|
|
|
package runner
|
2020-09-10 10:11:34 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"io/ioutil"
|
|
|
|
"log"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"time"
|
|
|
|
|
2020-09-28 13:46:37 +00:00
|
|
|
"github.com/gin-gonic/gin"
|
2020-09-10 10:11:34 +00:00
|
|
|
"gopkg.in/yaml.v2"
|
2020-09-17 12:07:34 +00:00
|
|
|
|
2020-09-28 13:46:37 +00:00
|
|
|
"nano-run/server"
|
|
|
|
"nano-run/server/ui"
|
2020-09-17 12:07:34 +00:00
|
|
|
"nano-run/worker"
|
2020-09-10 10:11:34 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type Config struct {
|
2020-09-28 13:46:37 +00:00
|
|
|
UIDirectory string `yaml:"ui_directory"`
|
2020-09-10 10:11:34 +00:00
|
|
|
WorkingDirectory string `yaml:"working_directory"`
|
|
|
|
ConfigDirectory string `yaml:"config_directory"`
|
|
|
|
Bind string `yaml:"bind"`
|
|
|
|
GracefulShutdown time.Duration `yaml:"graceful_shutdown"`
|
|
|
|
TLS struct {
|
|
|
|
Enable bool `yaml:"enable"`
|
|
|
|
Cert string `yaml:"cert"`
|
|
|
|
Key string `yaml:"key"`
|
|
|
|
} `yaml:"tls,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
|
|
|
defaultGracefulShutdown = 5 * time.Second
|
|
|
|
defaultBind = "127.0.0.1:8989"
|
|
|
|
)
|
|
|
|
|
|
|
|
func DefaultConfig() Config {
|
|
|
|
var cfg Config
|
|
|
|
cfg.Bind = defaultBind
|
|
|
|
cfg.WorkingDirectory = filepath.Join("run")
|
|
|
|
cfg.ConfigDirectory = filepath.Join("conf.d")
|
2020-09-28 13:46:37 +00:00
|
|
|
cfg.UIDirectory = filepath.Join("ui")
|
2020-09-10 10:11:34 +00:00
|
|
|
cfg.GracefulShutdown = defaultGracefulShutdown
|
|
|
|
return cfg
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cfg Config) CreateDirs() error {
|
|
|
|
err := os.MkdirAll(cfg.WorkingDirectory, 0755)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return os.MkdirAll(cfg.ConfigDirectory, 0755)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cfg *Config) LoadFile(file string) error {
|
|
|
|
data, err := ioutil.ReadFile(file)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = yaml.Unmarshal(data, cfg)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !filepath.IsAbs(cfg.WorkingDirectory) {
|
|
|
|
cfg.WorkingDirectory = filepath.Join(filepath.Dir(file), cfg.WorkingDirectory)
|
|
|
|
}
|
|
|
|
if !filepath.IsAbs(cfg.ConfigDirectory) {
|
|
|
|
cfg.ConfigDirectory = filepath.Join(filepath.Dir(file), cfg.ConfigDirectory)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cfg Config) SaveFile(file string) error {
|
|
|
|
data, err := yaml.Marshal(cfg)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return ioutil.WriteFile(file, data, 0600)
|
|
|
|
}
|
|
|
|
|
2020-09-17 12:07:34 +00:00
|
|
|
func (cfg Config) Create(global context.Context) (*Server, error) {
|
2020-09-28 13:46:37 +00:00
|
|
|
units, err := server.Units(cfg.ConfigDirectory)
|
2020-09-10 10:11:34 +00:00
|
|
|
if err != nil {
|
2020-09-17 12:07:34 +00:00
|
|
|
return nil, err
|
2020-09-10 10:11:34 +00:00
|
|
|
}
|
2020-09-28 13:46:37 +00:00
|
|
|
workers, err := server.Workers(cfg.WorkingDirectory, units)
|
2020-09-10 10:11:34 +00:00
|
|
|
if err != nil {
|
2020-09-17 12:07:34 +00:00
|
|
|
return nil, err
|
2020-09-10 10:11:34 +00:00
|
|
|
}
|
2020-09-17 12:07:34 +00:00
|
|
|
ctx, cancel := context.WithCancel(global)
|
2020-09-28 13:46:37 +00:00
|
|
|
|
|
|
|
router := gin.Default()
|
|
|
|
server.Attach(router.Group("/api/"), units, workers)
|
|
|
|
ui.Attach(router.Group("/ui/"), units, cfg.UIDirectory)
|
|
|
|
router.Group("/", func(gctx *gin.Context) {
|
|
|
|
gctx.Redirect(http.StatusTemporaryRedirect, "ui")
|
|
|
|
})
|
|
|
|
//router.Path("/").Methods("GET").HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
|
|
|
// http.Redirect(writer, request, "ui", http.StatusTemporaryRedirect)
|
|
|
|
//})
|
|
|
|
|
2020-09-17 12:07:34 +00:00
|
|
|
srv := &Server{
|
2020-09-28 13:46:37 +00:00
|
|
|
Handler: router,
|
2020-09-17 12:07:34 +00:00
|
|
|
workers: workers,
|
|
|
|
units: units,
|
|
|
|
done: make(chan struct{}),
|
|
|
|
cancel: cancel,
|
|
|
|
}
|
|
|
|
go srv.run(ctx)
|
|
|
|
return srv, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cfg Config) Run(global context.Context) error {
|
2020-09-10 10:11:34 +00:00
|
|
|
ctx, cancel := context.WithCancel(global)
|
2020-09-17 12:07:34 +00:00
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
srv, err := cfg.Create(global)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer srv.Close()
|
2020-09-10 10:11:34 +00:00
|
|
|
|
|
|
|
server := http.Server{
|
|
|
|
Addr: cfg.Bind,
|
2020-09-17 12:07:34 +00:00
|
|
|
Handler: srv,
|
2020-09-10 10:11:34 +00:00
|
|
|
}
|
|
|
|
|
2020-09-17 12:07:34 +00:00
|
|
|
done := make(chan struct{})
|
2020-09-10 10:11:34 +00:00
|
|
|
|
|
|
|
go func() {
|
|
|
|
defer cancel()
|
|
|
|
<-ctx.Done()
|
|
|
|
t, c := context.WithTimeout(context.Background(), cfg.GracefulShutdown)
|
|
|
|
_ = server.Shutdown(t)
|
|
|
|
c()
|
2020-09-17 12:07:34 +00:00
|
|
|
close(done)
|
2020-09-10 10:11:34 +00:00
|
|
|
}()
|
|
|
|
|
|
|
|
if cfg.TLS.Enable {
|
|
|
|
err = server.ListenAndServeTLS(cfg.TLS.Cert, cfg.TLS.Key)
|
|
|
|
} else {
|
|
|
|
err = server.ListenAndServe()
|
|
|
|
}
|
|
|
|
cancel()
|
2020-09-17 12:07:34 +00:00
|
|
|
<-done
|
2020-09-19 12:03:39 +00:00
|
|
|
return err
|
2020-09-10 10:11:34 +00:00
|
|
|
}
|
|
|
|
|
2020-09-17 12:07:34 +00:00
|
|
|
type Server struct {
|
|
|
|
http.Handler
|
|
|
|
workers []*worker.Worker
|
2020-09-28 13:46:37 +00:00
|
|
|
units []server.Unit
|
2020-09-17 12:07:34 +00:00
|
|
|
cancel func()
|
|
|
|
done chan struct{}
|
|
|
|
err error
|
|
|
|
}
|
|
|
|
|
2020-09-28 13:46:37 +00:00
|
|
|
func (srv *Server) Units() []server.Unit { return srv.units }
|
|
|
|
|
2020-09-17 12:07:34 +00:00
|
|
|
func (srv *Server) Close() {
|
|
|
|
for _, wrk := range srv.workers {
|
|
|
|
wrk.Close()
|
|
|
|
}
|
|
|
|
srv.cancel()
|
|
|
|
<-srv.done
|
|
|
|
}
|
|
|
|
|
|
|
|
func (srv *Server) Err() error {
|
|
|
|
return srv.err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (srv *Server) run(ctx context.Context) {
|
2020-09-28 13:46:37 +00:00
|
|
|
err := server.Run(ctx, srv.workers)
|
2020-09-17 12:07:34 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Println("workers stopped:", err)
|
|
|
|
}
|
|
|
|
srv.err = err
|
|
|
|
close(srv.done)
|
|
|
|
}
|