Browse Source

Add feature that pass the os.Kill signal to sub processes.

zry 3 years ago
parent
commit
84514513a0

+ 0 - 1
.gitignore

@@ -20,7 +20,6 @@ _cgo_export.*
 
 _testmain.go
 
-*.exe
 *.test
 *.prof
 

+ 10 - 2
defaultFrontend/main.go

@@ -12,6 +12,8 @@ var ListenAddress string
 var WWWRootPath string
 var BackendPrefix string
 var BackendTarget string
+var IsWindowsTestEnv string
+var ConfigFilePath string
 
 var ConfigData FrontendConfigYAML
 var AccessLogLevelConfigData *AccessLoggerLevelConfig
@@ -34,6 +36,7 @@ func GetConfigEnv() {
 	WWWRootPath = os.Getenv("wwwroot")
 	BackendPrefix = os.Getenv("backend_prefix")
 	BackendTarget = os.Getenv("backend_target")
+	IsWindowsTestEnv = os.Getenv("is_win_test_env")
 	if ListenAddress == "" {
 		fmt.Println("No environment variable 'listen', use default value ':8080'")
 		ListenAddress = ":8080"
@@ -50,6 +53,11 @@ func GetConfigEnv() {
 		fmt.Println("No environment variable 'backend_target', use default value 'http://localhost:9090/'")
 		BackendTarget = "http://localhost:9090/"
 	}
+	ConfigFilePath = "/data/frontend/zdwsi.frontend.config.yaml"
+	if IsWindowsTestEnv == "yes" {
+		fmt.Println("Windows Test Enviroment Mode Enabled.")
+		ConfigFilePath = "zdwsi.frontend.config.yaml"
+	}
 	fmt.Println("Listen Address: ", ListenAddress)
 	fmt.Println("WWW Root Path: ", WWWRootPath)
 	fmt.Println("Backend Prefix: ", BackendPrefix)
@@ -58,8 +66,8 @@ func GetConfigEnv() {
 }
 
 func ParseYAMLConfig() {
-	fmt.Println("Load Config '/data/frontend/zdwsi.frontend.config.yaml'...")
-	jdata, err := ioutil.ReadFile("/data/frontend/zdwsi.frontend.config.yaml")
+	fmt.Println("Load Config YAML ...")
+	jdata, err := ioutil.ReadFile(ConfigFilePath)
 	if err != nil {
 		fmt.Println("failed read config file: ", err)
 		os.Exit(ExitCode_YAMLFileReadError)

BIN
dist/rel/zDWSIFrontend_linux


BIN
dist/zDWSILauncher_linux


+ 3 - 0
dist_windows_test_env/.gitignore

@@ -0,0 +1,3 @@
+logs
+
+*.log

BIN
dist_windows_test_env/backend/Backend.exe


+ 60 - 0
dist_windows_test_env/config.yaml

@@ -0,0 +1,60 @@
+# config for frontend
+frontend:
+  # 'true' for enable frontend startup, 'false' for disable.
+  enable: true
+  # absolute path to the frontend executable file
+  entry: "zDWSIFrontend_WindowsTestEnv.exe"
+  # config for logger of frontend startup shell
+  shell_log:
+    # absolute path to shell log (log rotating will append infix into filename)
+    file: "logs/frontend.shell.log"
+    # max size per log file (MegaBytes)
+    max_size: 16
+    # max days for rotating
+    max_age: 30
+    # max backups for rotating remain
+    max_backups: 50
+    # 'true' for using local time for rotating
+    use_local_time: true
+    # 'true' for compress rotating history into zip files
+    compress_history: true
+  # listening address
+  # (will set environment variable 'listen' when startup frontend)
+  listen: ":18081"
+  # root path for frontend
+  # (will set environment variable 'wwwroot' when startup frontend)
+  wwwroot: "www/"
+  # config for backend reverse proxy
+  backend_proxy:
+    # backend forward prefix
+    # (will set environment variable 'backend_prefix' when startup frontend)
+    # e.g. : if using default frontend program, set this with 'api'
+    # the URL under http://hostname:port/api/ will be forwarding to backend
+    prefix: "api"
+    # backend forward target
+    # (will set environment variable 'backend_target' when startup frontend)
+    target: "http://localhost:9090/"
+
+# config for backend
+backend:
+  # 'true' for enable backend startup, 'false' for disable.
+  enable: true
+  # absolute path to the backend executable file
+  entry: "backend/Backend.exe"
+  # listening address
+  # (will set environment variable 'listen' when startup backend)
+  listen: ":9090"
+  # config for logger of backend startup shell
+  shell_log":
+    # absolute path to shell log (log rotating will append infix into filename)
+    file: "logs/backend.shell.log"
+    # max size per log file (MegaBytes)
+    max_size: 16
+    # max days for rotating
+    max_age: 30
+    # max backups for rotating remain
+    max_backups: 50
+    # 'true' for using local time for rotating
+    use_local_time: true
+    # 'true' for compress rotating history into zip files
+    compress_history: true

BIN
dist_windows_test_env/dlv.exe


+ 18 - 0
dist_windows_test_env/failure.html

@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset=utf-8>
+        <meta http-equiv=X-UA-Compatible content="IE=edge">
+        <meta name=viewport content="width=device-width,initial-scale=1">
+        <title>zDWSI Failure</title>
+    </head>
+    <body>
+        <h1>zDWSI Failure</h1>
+        <h2>Please contact web admin.</h2>
+        <hr />
+        <h6>
+            <a href="http://git.swzry.com/zry/zDWSI/" target="_blank">zDWSI</a>
+            - ZRY Docker Web Service Infrastructure - Version: 1.0
+        </h6>
+    </body>
+</html>

+ 18 - 0
dist_windows_test_env/www/index.html

@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset=utf-8>
+        <meta http-equiv=X-UA-Compatible content="IE=edge">
+        <meta name=viewport content="width=device-width,initial-scale=1">
+        <title>zDWSI Welcome Page</title>
+    </head>
+    <body>
+        <h1>Welcome!</h1>
+        <h2>zDWSI V1.0 deployed.</h2>
+        <hr />
+        <h6>
+            <a href="http://git.swzry.com/zry/zDWSI/" target="_blank">zDWSI</a>
+            - ZRY Docker Web Service Infrastructure - Version: 1.0
+        </h6>
+    </body>
+</html>

BIN
dist_windows_test_env/zDWSIFrontend_WindowsTestEnv.exe


BIN
dist_windows_test_env/zDWSILauncher_WindowsTestEnv.exe


+ 89 - 0
dist_windows_test_env/zdwsi.frontend.config.yaml

@@ -0,0 +1,89 @@
+# common config
+common:
+  # 'true' for enable global debug mode, 'false' for production environment
+  debug: true
+  # 'true' for redirect to 'redirect_url' when file not found, 'false' to return 404 message.
+  redirect_when_404: true
+  # when 'redirect_when_404' is 'true', it will redirect to this url when file not found.
+  redirect_url: "/"
+  # 'true' for enable backend reverse proxy, 'false' for disable.
+  backend_proxy_enable: true
+  # 'true' for enable gzip compression
+  enable_gzip: true
+  # 'true' for enable vue history mode support
+  vue_history_mode: true
+# config for log
+log:
+  # 'true' for enable access log, 'false' for disable access log
+  access_log_enable: true
+  # config for log rolling
+  rolling_log_config:
+    # config for error log rolling
+    error_log:
+      # absolute path to log (log rotating will append infix into filename)
+      file: "/data/logs/frontend.err.log"
+      # max size per log file (MegaBytes)
+      max_size: 16
+      # max days for rotating
+      max_age: 30
+      # max backups for rotating remain
+      max_backups: 50
+      # 'true' for using local time for rotating
+      use_local_time: true
+      # 'true' for compress rotating history into zip files
+      compress_history: true
+    # config for access log rolling
+    access_log:
+      # absolute path to log (log rotating will append infix into filename)
+      file: "/data/logs/frontend.access.log"
+      # max size per log file (MegaBytes)
+      max_size: 16
+      # max days for rotating
+      max_age: 30
+      # max backups for rotating remain
+      max_backups: 50
+      # 'true' for using local time for rotating
+      use_local_time: true
+      # 'true' for compress rotating history into zip files
+      compress_history: true
+  # detail config for access log
+  error_log_config:
+    # the log emitting level, the message which level below this will be emitted
+    # optional value: 'DEBUG', 'INFO', 'WARN', 'ERROR', 'PANIC', 'FATAL'
+    # all level option below should be one of these values
+    output_level: "INFO"
+    # format string for log. this formation using YAGTF.
+    # Reference: http://git.swzry.com/zry/YAGTF
+    # Expanded Tags: <level>, <unit>, <msg>
+    # Example: "![<y>-<mon>-<d> <h24>:<min>:<s>.<us> <tz> <tzA>] <lt><level><gt> {<unit>} <msg><br>"
+    log_format: "![<y>-<mon>-<d> <h24>:<min>:<s>.<us> <tz> <tzA>] <lt><level><gt> {<unit>} <msg><br>"
+    # 'true' for use UTC time in log, 'false' for use local timezone.
+    use_utc: false
+  # detail config for access log
+  access_log_config:
+    # the log emitting level, the message which level below this will be emitted
+    # optional value: 'DEBUG', 'INFO', 'WARN', 'ERROR', 'PANIC', 'FATAL'
+    # all level option below should be one of these values
+    output_level: "INFO"
+    # level for HTTP 200
+    success_level: "INFO"
+    # level for HTTP 404
+    not_found_level: "WARN"
+    # level for HTTP 403
+    forbidden_level: "WARN"
+    # level for HTTP 502/503
+    gateway_error_level: "ERROR"
+    # level for HTTP 500
+    internal_error_level: "ERROR"
+    # level for Other HTTP Status Code
+    other_status_level: "INFO"
+    # format string for log. this formation using YAGTF.
+    # Reference: http://git.swzry.com/zry/YAGTF
+    # Expanded Tags: <level>, <url>, <status>
+    # Example: "!{<q>time<q>: <q><y>-<mon>-<d> <h24>:<min>:<s>.<us> <tz> <tzA><q>, <q>level<q>: <q><level><q>, <q>method<q>: <q><method><q>, <q>status<q>: <status>, <q>client_ip<q>: <q><cip><q>, <q>url<q>: <q><url><q>}<br>"
+    # Output example for this example: {"time": "2019-09-19 11:45:14.191981000 +0800 CST", "level": "INFO", "method": "GET", "status": 200, "client_ip": "127.0.0.1", "url": "/"}
+    log_format: "!{<q>time<q>: <q><y>-<mon>-<d> <h24>:<min>:<s>.<us> <tz> <tzA><q>, <q>level<q>: <q><level><q>, <q>method<q>: <q><method><q>, <q>status<q>: <status>, <q>client_ip<q>: <q><cip><q>, <q>url<q>: <q><url><q>}<br>"
+    # 'true' for use UTC time in log, 'false' for use local timezone.
+    use_utc: false
+
+

+ 29 - 3
launcher/main.go

@@ -2,6 +2,7 @@ package main
 
 import (
 	"fmt"
+	go_lazy_quiter "git.swzry.com/zry/go-lazy-quiter"
 	"git.swzry.com/zry/pathutils"
 	"gopkg.in/natefinch/lumberjack.v2"
 	"gopkg.in/yaml.v2"
@@ -16,17 +17,34 @@ var ConfigData ConfigYAML
 var FrontendShellLogger *lumberjack.Logger
 var BackendShellLogger *lumberjack.Logger
 var mainWaitGroup sync.WaitGroup
+var lazyQuiter *go_lazy_quiter.LazyQuiter
+var pFrontend *os.Process
+var pBackend *os.Process
 
 func main() {
 	fmt.Println("zDWSI Launcher Start.")
 	CheckInit()
 	LoadYAML()
+	SetSignalCatcher()
 	StartFrontend()
 	StartBackend()
 	fmt.Println("Launcher Sleeping.")
 	RoutineSleep()
 }
 
+func SetSignalCatcher() {
+	lazyQuiter = go_lazy_quiter.NewLazyQuiter(os.Kill)
+	go func() {
+		lazyQuiter.Wait()
+		if pFrontend != nil {
+			_ = pFrontend.Signal(os.Kill)
+		}
+		if pBackend != nil {
+			_ = pBackend.Signal(os.Kill)
+		}
+	}()
+}
+
 func CheckInit() {
 	pe, err := pathutils.PathExists("/data/config.yaml")
 	if err == nil && pe {
@@ -103,7 +121,7 @@ func StartFrontend() {
 		runenv = append(runenv, makeEnvMember("wwwroot", ConfigData.Frontend.WWWRoot))
 		runenv = append(runenv, makeEnvMember("backend_prefix", ConfigData.Frontend.BackendProxy.Prefix))
 		runenv = append(runenv, makeEnvMember("backend_target", ConfigData.Frontend.BackendProxy.ProxyTaret))
-		err = StartProcess(ConfigData.Frontend.EntryPoint, runenv, FrontendShellLogger)
+		err = StartProcess(ConfigData.Frontend.EntryPoint, runenv, FrontendShellLogger, "frontend")
 		if err != nil {
 			fmt.Println("failed start frontend or frontend exited with error: ", err)
 		}
@@ -130,7 +148,7 @@ func StartBackend() {
 	go func() {
 		runenv := make([]string, 0)
 		runenv = append(runenv, makeEnvMember("listen", ConfigData.Backend.ListenAddr))
-		err = StartProcess(ConfigData.Backend.EntryPoint, runenv, BackendShellLogger)
+		err = StartProcess(ConfigData.Backend.EntryPoint, runenv, BackendShellLogger, "backend")
 		if err != nil {
 			fmt.Println("failed start backend or backend exited with error: ", err)
 		}
@@ -138,7 +156,7 @@ func StartBackend() {
 	}()
 }
 
-func StartProcess(entry string, runenv []string, logger io.Writer) error {
+func StartProcess(entry string, runenv []string, logger io.Writer, ptype string) error {
 	cmd := exec.Command(entry)
 	cmd.Env = runenv
 	pipe_r, pipe_w := io.Pipe()
@@ -148,6 +166,14 @@ func StartProcess(entry string, runenv []string, logger io.Writer) error {
 		io.Copy(logger, pipe_r)
 	}()
 	err := cmd.Start()
+	switch ptype {
+	case "frontend":
+		pFrontend = cmd.Process
+		break
+	case "backend":
+		pBackend = cmd.Process
+		break
+	}
 	if err != nil {
 		return err
 	}

+ 32 - 0
windows_test_env_launcher/failure_server.go

@@ -0,0 +1,32 @@
+package main
+
+import (
+	"fmt"
+	"io/ioutil"
+	"net/http"
+)
+
+var FailureListen string
+var FailureWebpage []byte
+
+func FailureServer() {
+	FailureListen = ":8080"
+	if ConfigData.Frontend.Enable {
+		if ConfigData.Frontend.ListenAddr != "" {
+			FailureListen = ConfigData.Frontend.ListenAddr
+		}
+	}
+	var err error
+	FailureWebpage, err = ioutil.ReadFile("failure.html")
+	if err != nil {
+		fmt.Println("Failed Load Failure Webpage.")
+		FailureWebpage = []byte("<h1>zDWSI Failure</h1>")
+	}
+	http.HandleFunc("/", handleFailurePage)
+	http.ListenAndServe(FailureListen, nil)
+}
+func handleFailurePage(writer http.ResponseWriter, request *http.Request) {
+	_ = request
+	writer.WriteHeader(502)
+	writer.Write(FailureWebpage)
+}

+ 199 - 0
windows_test_env_launcher/main.go

@@ -0,0 +1,199 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	go_lazy_quiter "git.swzry.com/zry/go-lazy-quiter"
+	"git.swzry.com/zry/pathutils"
+	"gopkg.in/natefinch/lumberjack.v2"
+	"gopkg.in/yaml.v2"
+	"io"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"sync"
+)
+
+var ConfigData ConfigYAML
+var FrontendShellLogger io.Writer
+var BackendShellLogger io.Writer
+var mainWaitGroup sync.WaitGroup
+var showHelp bool
+var withDLV bool
+var dlvListenString string
+var dlvProcess *os.Process
+var noFrontEnd bool
+
+func main() {
+	flag.BoolVar(&showHelp, "h", false, "Show this help")
+	flag.BoolVar(&withDLV, "d", false, "Start backend with delve debugger")
+	flag.StringVar(&dlvListenString, "l", "127.0.0.1:7827", "Set delve listening, default is '127.0.0.1:7827'")
+	flag.BoolVar(&noFrontEnd, "no-front", false, "Not to start front end")
+	flag.Parse()
+	if showHelp {
+		flag.PrintDefaults()
+		return
+	}
+	fmt.Println("zDWSI Windows Test Enviroment Launcher Start.")
+	LoadYAML()
+	if !noFrontEnd {
+		StartFrontend()
+	}
+	StartBackend()
+	//fmt.Println("Launcher Sleeping.")
+	//RoutineSleep()
+	q := go_lazy_quiter.NewLazyQuiter()
+	fmt.Println("Ctrl+C to quit.")
+	_ = q.Wait()
+	fmt.Println("Abort by user, quitting...")
+	if dlvProcess != nil {
+		err := dlvProcess.Kill()
+		if err != nil {
+			fmt.Println("failed kill delve:", err.Error())
+		}
+	}
+	fmt.Println("End.")
+	os.Exit(0)
+}
+
+func LoadYAML() {
+	fmt.Println("Load Config 'config.yaml'")
+	jdata, err := ioutil.ReadFile("config.yaml")
+	if err != nil {
+		fmt.Println("failed read file 'config.yaml': ", err)
+		Dead()
+	}
+	err = yaml.Unmarshal(jdata, &ConfigData)
+	if err != nil {
+		fmt.Println("failed parse 'config.yaml': ", err)
+		Dead()
+	}
+	fmt.Println("Config Loaded.")
+}
+
+func Dead() {
+	fmt.Println("Program Dead By Fatal Error. Please Restart.")
+	FailureServer()
+}
+
+func RoutineSleep() {
+	mainWaitGroup.Wait()
+	fmt.Println("All GoRoutine Stopped. Please Restart.")
+	FailureServer()
+}
+
+func InitLogger(cfg LumberjackLoggerYAML) io.Writer {
+	ljl := &lumberjack.Logger{
+		Filename:   cfg.FileName,
+		MaxSize:    cfg.MaxSize,
+		MaxAge:     cfg.MaxAge,
+		MaxBackups: cfg.MaxBackups,
+		LocalTime:  cfg.UseLocalTime,
+		Compress:   cfg.Compress,
+	}
+	wra := []io.Writer{ljl, os.Stdout}
+	mwr := io.MultiWriter(wra...)
+	return mwr
+}
+
+func StartFrontend() {
+	if !ConfigData.Frontend.Enable {
+		fmt.Println("Frontend Not Enable.")
+		return
+	}
+	pe, err := pathutils.PathExists(ConfigData.Frontend.EntryPoint)
+	if err != nil || !pe {
+		fmt.Println("frontend entrypoint not exists")
+		return
+	}
+	FrontendShellLogger = InitLogger(ConfigData.Frontend.ShellLog)
+	mainWaitGroup.Add(1)
+	go func() {
+		runenv := make([]string, 0)
+		runenv = append(runenv, makeEnvMember("listen", ConfigData.Frontend.ListenAddr))
+		runenv = append(runenv, makeEnvMember("wwwroot", ConfigData.Frontend.WWWRoot))
+		runenv = append(runenv, makeEnvMember("backend_prefix", ConfigData.Frontend.BackendProxy.Prefix))
+		runenv = append(runenv, makeEnvMember("backend_target", ConfigData.Frontend.BackendProxy.ProxyTaret))
+		runenv = append(runenv, "is_win_test_env=yes")
+		err = StartProcess(ConfigData.Frontend.EntryPoint, runenv, FrontendShellLogger)
+		if err != nil {
+			fmt.Println("failed start frontend or frontend exited with error: ", err)
+		}
+		mainWaitGroup.Done()
+	}()
+}
+
+func makeEnvMember(key string, value string) string {
+	return fmt.Sprintf("%s=%s", key, value)
+}
+
+func StartBackend() {
+	if !ConfigData.Backend.Enable {
+		fmt.Println("Backend Not Enable.")
+		return
+	}
+	pe, err := pathutils.PathExists(ConfigData.Backend.EntryPoint)
+	if err != nil || !pe {
+		fmt.Println("backend entrypoint not exists")
+		return
+	}
+	BackendShellLogger = InitLogger(ConfigData.Backend.ShellLog)
+	mainWaitGroup.Add(1)
+	go func() {
+		runenv := make([]string, 0)
+		runenv = append(runenv, makeEnvMember("listen", ConfigData.Backend.ListenAddr))
+		if withDLV {
+			err = StartProcessWithDLV(ConfigData.Backend.EntryPoint, runenv, BackendShellLogger)
+		} else {
+			err = StartProcess(ConfigData.Backend.EntryPoint, runenv, BackendShellLogger)
+		}
+		if err != nil {
+			fmt.Println("failed start backend or backend exited with error: ", err)
+		}
+		mainWaitGroup.Done()
+	}()
+}
+
+func StartProcess(entry string, runenv []string, logger io.Writer) error {
+	cmd := exec.Command(entry)
+	cmd.Env = runenv
+	pipe_r, pipe_w := io.Pipe()
+	cmd.Stdout = pipe_w
+	cmd.Stderr = pipe_w
+	go func() {
+		io.Copy(logger, pipe_r)
+	}()
+	err := cmd.Start()
+	if err != nil {
+		return err
+	}
+	err = cmd.Wait()
+	return err
+}
+
+func StartProcessWithDLV(entry string, runenv []string, logger io.Writer) error {
+	cmd := exec.Command("dlv")
+	cmd.Args = []string{
+		"dlv",
+		fmt.Sprintf("--listen=%s", dlvListenString),
+		"--headless=true",
+		"--api-version=2",
+		"--accept-multiclient",
+		"exec",
+		entry,
+	}
+	cmd.Env = runenv
+	pipe_r, pipe_w := io.Pipe()
+	cmd.Stdout = pipe_w
+	cmd.Stderr = pipe_w
+	go func() {
+		io.Copy(logger, pipe_r)
+	}()
+	err := cmd.Start()
+	dlvProcess = cmd.Process
+	if err != nil {
+		return err
+	}
+	err = cmd.Wait()
+	return err
+}

+ 36 - 0
windows_test_env_launcher/yamldef.go

@@ -0,0 +1,36 @@
+package main
+
+type ConfigYAML struct {
+	Frontend FrontendYAML `yaml:"frontend"`
+	Backend  BackendYAML  `yaml:"backend"`
+}
+
+type FrontendYAML struct {
+	Enable       bool                 `yaml:"enable"`
+	EntryPoint   string               `yaml:"entry"`
+	ListenAddr   string               `yaml:"listen"`
+	WWWRoot      string               `yaml:"wwwroot"`
+	BackendProxy BackendProxyYAML     `yaml:"backend_proxy"`
+	ShellLog     LumberjackLoggerYAML `yaml:"shell_log"`
+}
+
+type BackendYAML struct {
+	Enable     bool                 `yaml:"enable"`
+	EntryPoint string               `yaml:"entry"`
+	ListenAddr string               `yaml:"listen"`
+	ShellLog   LumberjackLoggerYAML `yaml:"shell_log"`
+}
+
+type BackendProxyYAML struct {
+	Prefix     string `yaml:"prefix"`
+	ProxyTaret string `yaml:"target"`
+}
+
+type LumberjackLoggerYAML struct {
+	FileName     string `yaml:"file"`
+	MaxSize      int    `yaml:"max_size"`
+	MaxAge       int    `yaml:"max_age"`
+	MaxBackups   int    `yaml:"max_backups"`
+	UseLocalTime bool   `yaml:"use_local_time"`
+	Compress     bool   `yaml:"compress_history"`
+}