v0.22.0 several enhancements and fixes for security, network, doc, test, packaging
Test / test (push) Canceled after 0s
Test / e2e (push) Canceled after 0s

This commit is contained in:
lixen committed 2026-10-03 14:52:16 -04:00
1 parent 9ab8c356d2
commit 71d1b82f83
102 files changed
+7102 -2178

No files matched your search

+45 -5
View File
@@ -18,8 +18,11 @@ var ErrConfigNotFound = lconfig.ErrConfigNotFound
// Manager owns the configuration sources and watcher for one application instance.
// Its snapshots are detached from each other and from the running service.
type Manager struct {
config *lconfig.Config
path string
config *lconfig.Config
path string
explicit bool
read bool // the file was loaded once
specs []pipelineSpec // command-line or environment pipelines, kept across reloads
}
// Load reads the startup sources and validates the initial configuration.
@@ -29,13 +32,30 @@ func Load(args []string) (*Manager, error) {
if err != nil {
return nil, err
}
specs, configArgs, err := takePipelineSpecs(configArgs)
if err != nil {
return nil, err
}
if len(specs) == 0 {
specs = envPipelineSpecs()
}
if _, err := buildPipelines(specs); err != nil {
return nil, err
}
m := &Manager{path: configPath, explicit: isExplicit, specs: specs}
initial := defaults()
cfg, err := lconfig.NewBuilder().
WithTarget(initial).
WithEnvPrefix("LOGWISP_").
WithArgs(configArgs).
WithFile(configPath).
WithTypedValidator(ValidateConfig).
WithTypedValidator(func(cfg *Config) error {
effective := *cfg // the builder keeps cfg; only the copy is replaced
if err := m.usePipelines(&effective); err != nil {
return err
}
return ValidateConfig(&effective)
}).
WithSecurityOptions(lconfig.SecurityOptions{
PreventPathTraversal: true,
MaxFileSize: 10 * 1024 * 1024,
@@ -50,10 +70,12 @@ func Load(args []string) (*Manager, error) {
}
// A missing discovered default still permits valid CLI/env/default values.
}
m.read = err == nil
if unknown := cfg.UnknownCLIKeys(); len(unknown) > 0 && !initial.Quiet {
fmt.Fprintf(os.Stderr, "Warning: unrecognized flags ignored: %v\n", unknown)
}
return &Manager{config: cfg, path: configPath}, nil
m.config = cfg
return m, nil
}
// Snapshot validates a detached candidate before it is used to build a service.
@@ -65,6 +87,9 @@ func (m *Manager) Snapshot() (*Config, error) {
}
cfg := value.(*Config)
cfg.ConfigFile = m.path
if err := m.usePipelines(cfg); err != nil {
return nil, err
}
if err := checkFileKeys(m.path); err != nil {
return nil, fmt.Errorf("config file %q: %w", m.path, err)
}
@@ -74,12 +99,27 @@ func (m *Manager) Snapshot() (*Config, error) {
return cfg, nil
}
// usePipelines replaces the file's or default pipelines with the spec ones.
func (m *Manager) usePipelines(cfg *Config) error {
if len(m.specs) == 0 {
return nil
}
pipelines, err := buildPipelines(m.specs)
cfg.Pipelines = pipelines
return err
}
// Reload rereads the selected file, retaining the startup CLI/environment sources.
// Signals must call this even when automatic watching is disabled.
func (m *Manager) Reload() (*Config, error) {
if err := m.config.LoadFile(m.path); err != nil {
// As at startup, a discovered default that does not exist is no error, so a
// file-less instance still rebuilds and rotates its certificates. Once read,
// its removal is one: the loader would keep the removed file's values.
err := m.config.LoadFile(m.path)
if err != nil && (m.explicit || m.read || !errors.Is(err, ErrConfigNotFound)) {
return nil, fmt.Errorf("reload %q: %w", m.path, err)
}
m.read = m.read || err == nil
return m.Snapshot()
}