Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 97d8a4541f | |||
| b8c43e0758 |
@@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import "context"
|
import "context"
|
||||||
import "flag"
|
import "flag"
|
||||||
|
import "fmt"
|
||||||
import "os"
|
import "os"
|
||||||
import "os/signal"
|
import "os/signal"
|
||||||
import "syscall"
|
import "syscall"
|
||||||
@@ -48,26 +49,34 @@ func run() int {
|
|||||||
var cfg codit.Config
|
var cfg codit.Config
|
||||||
var logger codit.Logger
|
var logger codit.Logger
|
||||||
var server *codit.Server
|
var server *codit.Server
|
||||||
|
var logFile string
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
flag.Var(&configPaths, "config", "path or wildcard pattern to YAML configuration file; repeatable")
|
flag.Var(&configPaths, "config", "path or wildcard pattern to YAML configuration file; repeatable")
|
||||||
flag.Var(&configPaths, "config-file", "path to YAML configuration file; repeatable")
|
flag.Var(&configPaths, "config-file", "path to YAML configuration file; repeatable")
|
||||||
flag.Var(&configPaths, "config-file-pattern", "wildcard pattern for YAML configuration files; repeatable")
|
flag.Var(&configPaths, "config-file-pattern", "wildcard pattern for YAML configuration files; repeatable")
|
||||||
|
flag.StringVar(&logFile, "log-file", "", "path to log file")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
logger = codit.NewAsyncLogger(PROGRAM_NAME, os.Stderr, codit.LOG_ALL)
|
cfg, err = codit.LoadConfigFilesWithEnvPrefix(configPaths, codit.EnvPrefixFromServerIdentifier(PROGRAM_NAME))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "load config: %v\n", err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if logFile != "" {
|
||||||
|
cfg.LogFile = logFile
|
||||||
|
}
|
||||||
|
logger, err = newLoggerFromConfig(PROGRAM_NAME, cfg)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "init logger: %v\n", err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
defer logger.Close()
|
defer logger.Close()
|
||||||
ctx, stop = signal.NotifyContext(context.Background(), syscall.SIGTERM, os.Interrupt)
|
ctx, stop = signal.NotifyContext(context.Background(), syscall.SIGTERM, os.Interrupt)
|
||||||
defer stop()
|
defer stop()
|
||||||
logger.Write("", codit.LOG_INFO, "starting %s %s", PROGRAM_NAME, PROGRAM_VERSION)
|
logger.Write("", codit.LOG_INFO, "starting %s %s", PROGRAM_NAME, PROGRAM_VERSION)
|
||||||
defer logger.Write("", codit.LOG_INFO, "stopped %s %s", PROGRAM_NAME, PROGRAM_VERSION)
|
defer logger.Write("", codit.LOG_INFO, "stopped %s %s", PROGRAM_NAME, PROGRAM_VERSION)
|
||||||
|
|
||||||
cfg, err = codit.LoadConfigFilesWithEnvPrefix(configPaths, codit.EnvPrefixFromServerIdentifier(PROGRAM_NAME))
|
|
||||||
if err != nil {
|
|
||||||
logger.Write("", codit.LOG_ERROR, "load config: %v", err)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
server, err = codit.NewServerWithIdentifier(&cfg, logger, PROGRAM_NAME)
|
server, err = codit.NewServerWithIdentifier(&cfg, logger, PROGRAM_NAME)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Write("", codit.LOG_ERROR, "init app: %v", err)
|
logger.Write("", codit.LOG_ERROR, "init app: %v", err)
|
||||||
@@ -83,3 +92,19 @@ func run() int {
|
|||||||
logger.Write("", codit.LOG_INFO, "shutdown complete - %s %s", PROGRAM_NAME, PROGRAM_VERSION)
|
logger.Write("", codit.LOG_INFO, "shutdown complete - %s %s", PROGRAM_NAME, PROGRAM_VERSION)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newLoggerFromConfig(id string, cfg codit.Config) (codit.Logger, error) {
|
||||||
|
var mask codit.LogMask
|
||||||
|
var logger *codit.AsyncLogger
|
||||||
|
var err error
|
||||||
|
|
||||||
|
mask = codit.LogStrsToMask(cfg.LogMask)
|
||||||
|
if cfg.LogFile == "" {
|
||||||
|
return codit.NewAsyncLogger(id, os.Stderr, mask), nil
|
||||||
|
}
|
||||||
|
logger, err = codit.NewAsyncLoggerToFile(id, cfg.LogFile, cfg.LogMaxSize, cfg.LogRotate, mask)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return logger, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -47,6 +47,18 @@ type Config struct {
|
|||||||
DockerHTTPPrefix string `yaml:"docker_http_prefix"`
|
DockerHTTPPrefix string `yaml:"docker_http_prefix"`
|
||||||
GitHTTPPrefix string `yaml:"git_http_prefix"`
|
GitHTTPPrefix string `yaml:"git_http_prefix"`
|
||||||
RPMHTTPPrefix string `yaml:"rpm_http_prefix"`
|
RPMHTTPPrefix string `yaml:"rpm_http_prefix"`
|
||||||
|
App LogConfig `yaml:"app"`
|
||||||
|
LogMask []string `yaml:"log-mask"`
|
||||||
|
LogFile string `yaml:"log-file"`
|
||||||
|
LogMaxSize int64 `yaml:"log-max-size"`
|
||||||
|
LogRotate int `yaml:"log-rotate"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LogConfig struct {
|
||||||
|
LogMask []string `yaml:"log-mask"`
|
||||||
|
LogFile string `yaml:"log-file"`
|
||||||
|
LogMaxSize int64 `yaml:"log-max-size"`
|
||||||
|
LogRotate int `yaml:"log-rotate"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const DefaultEnvPrefix string = "CODIT_"
|
const DefaultEnvPrefix string = "CODIT_"
|
||||||
@@ -89,11 +101,13 @@ func LoadFilesWithEnvPrefix(paths []string, envPrefix string) (Config, error) {
|
|||||||
DockerHTTPPrefix: "/v2",
|
DockerHTTPPrefix: "/v2",
|
||||||
GitHTTPPrefix: "/git",
|
GitHTTPPrefix: "/git",
|
||||||
RPMHTTPPrefix: "/rpm",
|
RPMHTTPPrefix: "/rpm",
|
||||||
|
LogMask: []string{},
|
||||||
}
|
}
|
||||||
err = loadConfigFiles(&cfg, paths)
|
err = loadConfigFiles(&cfg, paths)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cfg, err
|
return cfg, err
|
||||||
}
|
}
|
||||||
|
applyAppLogConfig(&cfg)
|
||||||
override(&cfg, envPrefix)
|
override(&cfg, envPrefix)
|
||||||
cfg.AuthMode = strings.ToLower(strings.TrimSpace(cfg.AuthMode))
|
cfg.AuthMode = strings.ToLower(strings.TrimSpace(cfg.AuthMode))
|
||||||
cfg.TLSServerCertSource = strings.ToLower(strings.TrimSpace(cfg.TLSServerCertSource))
|
cfg.TLSServerCertSource = strings.ToLower(strings.TrimSpace(cfg.TLSServerCertSource))
|
||||||
@@ -112,6 +126,21 @@ func LoadFilesWithEnvPrefix(paths []string, envPrefix string) (Config, error) {
|
|||||||
return cfg, nil
|
return cfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func applyAppLogConfig(cfg *Config) {
|
||||||
|
if len(cfg.App.LogMask) > 0 {
|
||||||
|
cfg.LogMask = cfg.App.LogMask
|
||||||
|
}
|
||||||
|
if cfg.App.LogFile != "" {
|
||||||
|
cfg.LogFile = cfg.App.LogFile
|
||||||
|
}
|
||||||
|
if cfg.App.LogMaxSize != 0 {
|
||||||
|
cfg.LogMaxSize = cfg.App.LogMaxSize
|
||||||
|
}
|
||||||
|
if cfg.App.LogRotate != 0 {
|
||||||
|
cfg.LogRotate = cfg.App.LogRotate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
func loadConfigFiles(cfg *Config, paths []string) error {
|
func loadConfigFiles(cfg *Config, paths []string) error {
|
||||||
var expanded []string
|
var expanded []string
|
||||||
@@ -321,6 +350,22 @@ func override(cfg *Config, envPrefix string) {
|
|||||||
if v != "" {
|
if v != "" {
|
||||||
cfg.RPMHTTPPrefix = v
|
cfg.RPMHTTPPrefix = v
|
||||||
}
|
}
|
||||||
|
v = getEnv(envPrefix, "LOG_MASK")
|
||||||
|
if v != "" {
|
||||||
|
cfg.LogMask = splitCSV(v)
|
||||||
|
}
|
||||||
|
v = getEnv(envPrefix, "LOG_FILE")
|
||||||
|
if v != "" {
|
||||||
|
cfg.LogFile = v
|
||||||
|
}
|
||||||
|
v = getEnv(envPrefix, "LOG_MAX_SIZE")
|
||||||
|
if v != "" {
|
||||||
|
cfg.LogMaxSize, _ = strconv.ParseInt(v, 10, 64)
|
||||||
|
}
|
||||||
|
v = getEnv(envPrefix, "LOG_ROTATE")
|
||||||
|
if v != "" {
|
||||||
|
cfg.LogRotate, _ = strconv.Atoi(v)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getEnv(prefix string, name string) string {
|
func getEnv(prefix string, name string) string {
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ func TestLoadFromYAMLAndEnvOverride(t *testing.T) {
|
|||||||
var cfg Config
|
var cfg Config
|
||||||
dir = t.TempDir()
|
dir = t.TempDir()
|
||||||
path = filepath.Join(dir, "config.yaml")
|
path = filepath.Join(dir, "config.yaml")
|
||||||
data = "db_driver: sqlite\ndb_dsn: file:test.db\nauth_mode: HyBrId\ngit_http_prefix: /g\ndocker_http_prefix: registry\n"
|
data = "db_driver: sqlite\ndb_dsn: file:test.db\nauth_mode: HyBrId\ngit_http_prefix: /g\ndocker_http_prefix: registry\napp:\n log-mask: [error, warn]\n log-file: /tmp/codit.log\n log-max-size: 1234\n log-rotate: 3\n"
|
||||||
err = os.WriteFile(path, []byte(data), 0o644)
|
err = os.WriteFile(path, []byte(data), 0o644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("write config file: %v", err)
|
t.Fatalf("write config file: %v", err)
|
||||||
@@ -81,6 +81,12 @@ func TestLoadFromYAMLAndEnvOverride(t *testing.T) {
|
|||||||
if cfg.DockerHTTPPrefix != "/registry" {
|
if cfg.DockerHTTPPrefix != "/registry" {
|
||||||
t.Fatalf("docker_http_prefix normalization failed: %s", cfg.DockerHTTPPrefix)
|
t.Fatalf("docker_http_prefix normalization failed: %s", cfg.DockerHTTPPrefix)
|
||||||
}
|
}
|
||||||
|
if len(cfg.LogMask) != 2 || cfg.LogMask[0] != "error" || cfg.LogMask[1] != "warn" {
|
||||||
|
t.Fatalf("log-mask load failed: %#v", cfg.LogMask)
|
||||||
|
}
|
||||||
|
if cfg.LogFile != "/tmp/codit.log" || cfg.LogMaxSize != 1234 || cfg.LogRotate != 3 {
|
||||||
|
t.Fatalf("log config load failed: file=%q max=%d rotate=%d", cfg.LogFile, cfg.LogMaxSize, cfg.LogRotate)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLoadFilesLaterWinsAndGlob(t *testing.T) {
|
func TestLoadFilesLaterWinsAndGlob(t *testing.T) {
|
||||||
|
|||||||
+40
-5
@@ -14,6 +14,7 @@ import "io"
|
|||||||
import "log"
|
import "log"
|
||||||
import "net"
|
import "net"
|
||||||
import "net/http"
|
import "net/http"
|
||||||
|
import "net/netip"
|
||||||
import "os"
|
import "os"
|
||||||
import "path/filepath"
|
import "path/filepath"
|
||||||
import "sync"
|
import "sync"
|
||||||
@@ -1221,14 +1222,22 @@ func (m *additionalListenerManager) startEndpoint(endpoint listenerEndpoint) run
|
|||||||
m.wg.Add(1)
|
m.wg.Add(1)
|
||||||
go func(ep listenerEndpoint, srv *http.Server) {
|
go func(ep listenerEndpoint, srv *http.Server) {
|
||||||
var err error
|
var err error
|
||||||
|
var listener net.Listener
|
||||||
|
var network string
|
||||||
defer m.wg.Done()
|
defer m.wg.Done()
|
||||||
|
network = tcpAddrStrClass(ep.Addr)
|
||||||
|
listener, err = net.Listen(network, ep.Addr)
|
||||||
|
if err != nil {
|
||||||
|
m.Logger.Write(ep.Name, LOG_ERROR, "additional listener failed network=%s addr=%s err=%v", network, ep.Addr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
if ep.IsHTTPS {
|
if ep.IsHTTPS {
|
||||||
err = srv.ListenAndServeTLS("", "")
|
err = srv.ServeTLS(listener, "", "")
|
||||||
} else {
|
} else {
|
||||||
err = srv.ListenAndServe()
|
err = srv.Serve(listener)
|
||||||
}
|
}
|
||||||
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||||
m.Logger.Write(ep.Name, LOG_ERROR, "additional listener failed addr=%s err=%v", ep.Addr, err)
|
m.Logger.Write(ep.Name, LOG_ERROR, "additional listener failed network=%s addr=%s err=%v", network, ep.Addr, err)
|
||||||
}
|
}
|
||||||
}(endpoint, server)
|
}(endpoint, server)
|
||||||
return running
|
return running
|
||||||
@@ -1935,6 +1944,24 @@ func TitleFromServerIdentifier(value string) string {
|
|||||||
return codit_config.TitleFromServerIdentifier(value)
|
return codit_config.TitleFromServerIdentifier(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func tcpAddrStrClass(addr string) string {
|
||||||
|
var ap netip.AddrPort
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if len(addr) > 0 {
|
||||||
|
ap, err = netip.ParseAddrPort(addr)
|
||||||
|
if err == nil {
|
||||||
|
if ap.Addr().Is6() {
|
||||||
|
return "tcp6"
|
||||||
|
}
|
||||||
|
if ap.Addr().Is4() || ap.Addr().Is4In6() {
|
||||||
|
return "tcp4"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "tcp"
|
||||||
|
}
|
||||||
|
|
||||||
func serveListeners(ctx context.Context, endpoints []listenerEndpoint, handler http.Handler, logger Logger, serverIdentifier string) error {
|
func serveListeners(ctx context.Context, endpoints []listenerEndpoint, handler http.Handler, logger Logger, serverIdentifier string) error {
|
||||||
var listenCtx context.Context
|
var listenCtx context.Context
|
||||||
var cancel context.CancelFunc
|
var cancel context.CancelFunc
|
||||||
@@ -1980,9 +2007,12 @@ func serveListeners(ctx context.Context, endpoints []listenerEndpoint, handler h
|
|||||||
var err error
|
var err error
|
||||||
var server *http.Server
|
var server *http.Server
|
||||||
var l *log.Logger
|
var l *log.Logger
|
||||||
|
var listener net.Listener
|
||||||
|
var network string
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
l = log.New(&server_http_log_writer{l: logger, id: "", depth: +2}, "", 0)
|
l = log.New(&server_http_log_writer{l: logger, id: "", depth: +2}, "", 0)
|
||||||
|
network = tcpAddrStrClass(endpoint.Addr)
|
||||||
if endpoint.IsHTTPS {
|
if endpoint.IsHTTPS {
|
||||||
server = &http.Server{
|
server = &http.Server{
|
||||||
Addr: endpoint.Addr,
|
Addr: endpoint.Addr,
|
||||||
@@ -2002,10 +2032,15 @@ func serveListeners(ctx context.Context, endpoints []listenerEndpoint, handler h
|
|||||||
serversMu.Lock()
|
serversMu.Lock()
|
||||||
servers = append(servers, server)
|
servers = append(servers, server)
|
||||||
serversMu.Unlock()
|
serversMu.Unlock()
|
||||||
|
listener, err = net.Listen(network, endpoint.Addr)
|
||||||
|
if err != nil {
|
||||||
|
errs <- fmt.Errorf("listen %s %s: %w", network, endpoint.Addr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
if endpoint.IsHTTPS {
|
if endpoint.IsHTTPS {
|
||||||
err = server.ListenAndServeTLS("", "")
|
err = server.ServeTLS(listener, "", "")
|
||||||
} else {
|
} else {
|
||||||
err = server.ListenAndServe()
|
err = server.Serve(listener)
|
||||||
}
|
}
|
||||||
errs <- err
|
errs <- err
|
||||||
}(ep)
|
}(ep)
|
||||||
|
|||||||
Reference in New Issue
Block a user