[0.30] support web.listen-addr from CLI (#3)

Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
Signed-off-by: Jan-Otto Kröpke <github@jkroepke.de>
This commit is contained in:
Jan-Otto Kröpke
2025-04-06 03:36:58 +02:00
committed by GitHub
parent 759faee1c3
commit 2c4698f119
2 changed files with 58 additions and 67 deletions

View File

@@ -74,7 +74,7 @@ func run(ctx context.Context, args []string) int {
"config.file", "config.file",
"YAML configuration file to use. Values set in this file will be overridden by CLI flags.", "YAML configuration file to use. Values set in this file will be overridden by CLI flags.",
).String() ).String()
insecureSkipVerify = app.Flag( _ = app.Flag(
"config.file.insecure-skip-verify", "config.file.insecure-skip-verify",
"Skip TLS verification in loading YAML configuration.", "Skip TLS verification in loading YAML configuration.",
).Default("false").Bool() ).Default("false").Bool()
@@ -125,11 +125,9 @@ func run(ctx context.Context, args []string) int {
// Initialize collectors before loading and parsing CLI arguments // Initialize collectors before loading and parsing CLI arguments
collectors := collector.NewWithFlags(app) collectors := collector.NewWithFlags(app)
// Load values from configuration file(s). Executable flags must first be parsed, in order if err := config.Parse(app, os.Args[1:]); err != nil {
// to load the specified file(s).
if _, err := app.Parse(os.Args[1:]); err != nil {
//nolint:sloglint // we do not have an logger yet //nolint:sloglint // we do not have an logger yet
slog.LogAttrs(ctx, slog.LevelError, "Failed to parse CLI args", slog.LogAttrs(ctx, slog.LevelError, "Failed to load configuration",
slog.Any("err", err), slog.Any("err", err),
) )
@@ -137,58 +135,16 @@ func run(ctx context.Context, args []string) int {
} }
debug.SetMemoryLimit(*memoryLimit) debug.SetMemoryLimit(*memoryLimit)
logger, err := log.New(logConfig) logger, err := log.New(logConfig)
if err != nil { if err != nil {
logger.LogAttrs(ctx, slog.LevelError, "failed to create logger", logger.LogAttrs(ctx, slog.LevelError, "failed to create logger",
slog.Any("err", err), slog.Any("err", err),
) )
return 1 return 1
} }
if *configFile != "" { if configFile != nil && *configFile != "" {
resolver, err := config.NewResolver(ctx, *configFile, logger, *insecureSkipVerify) logger.InfoContext(ctx, "using configuration file: "+*configFile)
if err != nil {
logger.Error("could not load config file",
slog.Any("err", err),
)
return 1
}
if err = resolver.Bind(app, os.Args[1:]); err != nil {
logger.ErrorContext(ctx, "failed to bind configuration",
slog.Any("err", err),
)
return 1
}
// Parse flags once more to include those discovered in configuration file(s).
if _, err = app.Parse(os.Args[1:]); err != nil {
logger.ErrorContext(ctx, "failed to parse CLI args from YAML file",
slog.Any("err", err),
)
return 1
}
// NOTE: This is temporary fix for issue #1092, calling kingpin.Parse
// twice makes slices flags duplicate its value, this clean up
// the first parse before the second call.
slices.Sort(*webConfig.WebListenAddresses)
*webConfig.WebListenAddresses = slices.Clip(slices.Compact(*webConfig.WebListenAddresses))
logger, err = log.New(logConfig)
if err != nil {
//nolint:sloglint // we do not have an logger yet
slog.Error("failed to create logger",
slog.Any("err", err),
)
return 1
}
} }
logger.LogAttrs(ctx, slog.LevelDebug, "logging has Started") logger.LogAttrs(ctx, slog.LevelDebug, "logging has Started")

View File

@@ -16,7 +16,6 @@
package config package config
import ( import (
"context"
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"io" "io"
@@ -38,8 +37,52 @@ type Resolver struct {
flags map[string]string flags map[string]string
} }
// NewResolver returns a Resolver structure. // Parse parses the command line arguments and configuration files.
func NewResolver(ctx context.Context, file string, logger *slog.Logger, insecureSkipVerify bool) (*Resolver, error) { func Parse(app *kingpin.Application, args []string) error {
configFile := ParseConfigFile(args)
if configFile != "" {
resolver, err := NewConfigFileResolver(configFile)
if err != nil {
return fmt.Errorf("failed to load configuration file: %w", err)
}
if err = resolver.Bind(app, args); err != nil {
return fmt.Errorf("failed to bind configuration: %w", err)
}
}
if _, err := app.Parse(args); err != nil {
return fmt.Errorf("failed to parse flags: %w", err)
}
return nil
}
// ParseConfigFile manually parses the configuration file from the command line arguments.
func ParseConfigFile(args []string) string {
for i, cliFlag := range args {
if strings.HasPrefix(cliFlag, "--config.file=") {
return strings.TrimPrefix(cliFlag, "--config.file=")
}
if strings.HasPrefix(cliFlag, "-config.file=") {
return strings.TrimPrefix(cliFlag, "-config.file=")
}
if strings.HasSuffix(cliFlag, "-config.file") {
if len(os.Args) <= i+1 {
return ""
}
return os.Args[i+1]
}
}
return ""
}
// NewConfigFileResolver returns a Resolver structure.
func NewConfigFileResolver(file string) (*Resolver, error) {
flags := map[string]string{} flags := map[string]string{}
var ( var (
@@ -48,14 +91,14 @@ func NewResolver(ctx context.Context, file string, logger *slog.Logger, insecure
) )
if strings.HasPrefix(file, "http://") || strings.HasPrefix(file, "https://") { if strings.HasPrefix(file, "http://") || strings.HasPrefix(file, "https://") {
logger.WarnContext(ctx, "Loading configuration file from URL is deprecated and will be removed in 0.31.0. Use a local file instead.") slog.Warn("Loading configuration file from URL is deprecated and will be removed in 0.31.0. Use a local file instead.")
fileBytes, err = readFromURL(ctx, file, logger, insecureSkipVerify) fileBytes, err = readFromURL(file)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} else { } else {
fileBytes, err = readFromFile(ctx, file, logger) fileBytes, err = readFromFile(file)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -79,9 +122,7 @@ func NewResolver(ctx context.Context, file string, logger *slog.Logger, insecure
return &Resolver{flags: flags}, nil return &Resolver{flags: flags}, nil
} }
func readFromFile(ctx context.Context, file string, logger *slog.Logger) ([]byte, error) { func readFromFile(file string) ([]byte, error) {
logger.InfoContext(ctx, "loading configuration file: "+file)
if _, err := os.Stat(file); err != nil { if _, err := os.Stat(file); err != nil {
return nil, fmt.Errorf("failed to read configuration file: %w", err) return nil, fmt.Errorf("failed to read configuration file: %w", err)
} }
@@ -94,20 +135,14 @@ func readFromFile(ctx context.Context, file string, logger *slog.Logger) ([]byte
return fileBytes, nil return fileBytes, nil
} }
func readFromURL(ctx context.Context, file string, logger *slog.Logger, insecureSkipVerify bool) ([]byte, error) { func readFromURL(file string) ([]byte, error) {
logger.InfoContext(ctx, "loading configuration file from URL: "+file)
tr := &http.Transport{ tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: insecureSkipVerify}, //nolint:gosec TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //nolint:gosec
}
if insecureSkipVerify {
logger.WarnContext(ctx, "Loading configuration file with TLS verification disabled")
} }
client := &http.Client{Transport: tr} client := &http.Client{Transport: tr}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, file, nil) req, err := http.NewRequest(http.MethodGet, file, nil)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create HTTP request: %w", err) return nil, fmt.Errorf("failed to create HTTP request: %w", err)
} }