v0.28.0 option schema in struct tags, schema replacing plugin factory metadata, tui prep

This commit is contained in:
lixen committed 2026-10-09 14:43:44 -04:00
1 parent a1648ce17b
commit e8e5f05e47
43 files changed
+1345 -1211

No files matched your search

+3 -22
View File
@@ -25,13 +25,6 @@ func init() {
panic(fmt.Sprintf("failed to register console source: %v", err))
}
// Console stdin can only have one reader
if err := plugin.SetSourceMetadata("console", &plugin.PluginMetadata{
Capabilities: []core.Capability{core.CapSessionAware, core.CapSingleInstance},
MaxInstances: 1,
}); err != nil {
panic(fmt.Sprintf("failed to set console source metadata: %v", err))
}
}
// One reader serves the process: after a reload the new source continues
@@ -114,10 +107,6 @@ type ConsoleSource struct {
ended atomic.Bool // standard input reached its end
}
const (
DefaultConsoleSourceBufferSize = 1000
)
// NewConsoleSourcePlugin creates a console source through plugin factory
func NewConsoleSourcePlugin(
id string,
@@ -125,19 +114,11 @@ func NewConsoleSourcePlugin(
logger *log.Logger,
proxy *session.Proxy,
) (source.Source, error) {
opts := &config.ConsoleSourceOptions{}
// Scan config map
if err := config.Scan(configMap, opts); err != nil {
return nil, fmt.Errorf("failed to parse config: %w", err)
opts, err := config.Decode[config.ConsoleSourceOptions]("source", "console", configMap)
if err != nil {
return nil, err
}
// Validate and apply defaults
if opts.BufferSize <= 0 {
opts.BufferSize = DefaultConsoleSourceBufferSize
}
// Create and return plugin instance
cs := &ConsoleSource{
id: id,
proxy: proxy,
+3 -33
View File
@@ -19,7 +19,6 @@ import (
"github.com/lixenwraith/logwisp/internal/session"
"github.com/lixenwraith/logwisp/internal/source"
lconfig "github.com/lixenwraith/config"
"github.com/lixenwraith/log"
)
@@ -64,13 +63,6 @@ type FileSource struct {
lastEntryTime atomic.Value // time.Time
}
const (
DefaultFileSourcePattern = "*"
DefaultFileSourceCheckIntervalMS = 100
MinFileSourceCheckIntervalMS = 10
DefaultFileSourceFrom = "end"
)
// NewFileSourcePlugin creates a file source through plugin factory
func NewFileSourcePlugin(
id string,
@@ -78,33 +70,11 @@ func NewFileSourcePlugin(
logger *log.Logger,
proxy *session.Proxy,
) (source.Source, error) {
opts := &config.FileSourceOptions{}
// Use lconfig to scan map into struct (overriding defaults)
if err := config.Scan(configMap, opts); err != nil {
return nil, fmt.Errorf("failed to parse config: %w", err)
opts, err := config.Decode[config.FileSourceOptions]("source", "file", configMap)
if err != nil {
return nil, err
}
// Validate and apply defaults
if err := lconfig.NonEmpty(opts.Directory); err != nil {
return nil, fmt.Errorf("directory: %w", err)
}
if opts.Pattern == "" {
opts.Pattern = DefaultFileSourcePattern
}
if opts.CheckIntervalMS <= 0 {
opts.CheckIntervalMS = DefaultFileSourceCheckIntervalMS
} else if opts.CheckIntervalMS < MinFileSourceCheckIntervalMS {
return nil, fmt.Errorf("check_interval_ms: must be >= %d", MinFileSourceCheckIntervalMS)
}
if opts.From == "" {
opts.From = DefaultFileSourceFrom
} else if err := lconfig.OneOf("start", "end")(opts.From); err != nil {
return nil, fmt.Errorf("from: %w", err)
}
// Create and return plugin instance
fs := &FileSource{
id: id,
proxy: proxy,
+7 -35
View File
@@ -10,7 +10,6 @@ import (
"net"
"net/http"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
@@ -25,7 +24,6 @@ import (
"github.com/lixenwraith/logwisp/internal/source"
"github.com/lixenwraith/logwisp/internal/tlsx"
lconfig "github.com/lixenwraith/config"
"github.com/lixenwraith/log"
)
@@ -36,12 +34,8 @@ func init() {
}
const (
DefaultHTTPChainSourceBufferSize = 1000
DefaultHTTPChainSourceIngestPath = "/ingest"
DefaultHTTPChainSourceMaxBodyBytes = 8 * 1024 * 1024
DefaultHTTPChainSourceReadTimeoutMS = 30000
HTTPChainReadHeaderTimeout = 10 * time.Second
HTTPChainServerShutdownTimeout = 2 * time.Second
HTTPChainReadHeaderTimeout = 10 * time.Second
HTTPChainServerShutdownTimeout = 2 * time.Second
)
// HTTPChainSource accepts NDJSON batches from upstream http_chain sinks
@@ -84,35 +78,13 @@ func NewHTTPChainSourcePlugin(
logger *log.Logger,
proxy *session.Proxy,
) (source.Source, error) {
opts := &config.HTTPChainSourceOptions{
Host: "0.0.0.0",
TrustNode: true,
}
if err := config.Scan(configMap, opts); err != nil {
return nil, fmt.Errorf("failed to parse config: %w", err)
}
if err := lconfig.Port(opts.Port); err != nil {
return nil, fmt.Errorf("port: %w", err)
opts, err := config.Decode[config.HTTPChainSourceOptions]("source", "http_chain", configMap)
if err != nil {
return nil, err
}
network, err := core.Network(opts.Host)
if err != nil {
return nil, fmt.Errorf("host: %w", err)
}
if opts.IngestPath == "" {
opts.IngestPath = DefaultHTTPChainSourceIngestPath
} else if !strings.HasPrefix(opts.IngestPath, "/") {
return nil, fmt.Errorf("ingest_path: must start with '/'")
} else if opts.IngestPath == chain.AuthPath {
return nil, fmt.Errorf("ingest_path: %s is reserved for authentication", chain.AuthPath)
}
if opts.BufferSize <= 0 {
opts.BufferSize = DefaultHTTPChainSourceBufferSize
}
if opts.MaxBodyBytes <= 0 {
opts.MaxBodyBytes = DefaultHTTPChainSourceMaxBodyBytes
}
if opts.ReadTimeoutMS <= 0 {
opts.ReadTimeoutMS = DefaultHTTPChainSourceReadTimeoutMS
return nil, err
}
tlsCfg, err := tlsx.Server(opts.TLS, opts.Host)
if err != nil {
@@ -122,7 +94,7 @@ func NewHTTPChainSourcePlugin(
if err != nil {
return nil, err
}
authPolicy, err := authz.New(opts.Auth, tlsCfg, aclPolicy, authz.RoleChainListener, authz.HTTP)
authPolicy, err := authz.New(opts.Auth, tlsCfg, aclPolicy, config.ChainListener, authz.HTTP)
if err != nil {
return nil, err
}
+2 -2
View File
@@ -48,8 +48,8 @@ func NewNullSourcePlugin(
logger *log.Logger,
proxy *session.Proxy,
) (source.Source, error) {
if err := config.Scan(configMap, &config.NullSourceOptions{}); err != nil {
return nil, fmt.Errorf("failed to parse config: %w", err)
if _, err := config.Decode[config.NullSourceOptions]("source", "null", configMap); err != nil {
return nil, err
}
ns := &NullSource{
id: id,
+3 -42
View File
@@ -14,7 +14,6 @@ import (
"github.com/lixenwraith/logwisp/internal/session"
"github.com/lixenwraith/logwisp/internal/source"
lconfig "github.com/lixenwraith/config"
"github.com/lixenwraith/log"
)
@@ -53,12 +52,6 @@ type RandomSource struct {
lastEntryTime atomic.Value // time.Time
}
const (
DefaultRandomSourceIntervalMS = 500
DefaultRandomSourceFormat = "txt"
DefaultRandomSourceLength = 20
)
// NewRandomSourcePlugin creates a random source through plugin factory
func NewRandomSourcePlugin(
id string,
@@ -67,41 +60,9 @@ func NewRandomSourcePlugin(
proxy *session.Proxy,
) (source.Source, error) {
// Step 1: Create empty config struct with defaults
opts := &config.RandomSourceOptions{
IntervalMS: 500,
JitterMS: 0,
Format: "txt",
Length: 20,
Special: false,
}
// Scan config map
if err := config.Scan(configMap, opts); err != nil {
return nil, fmt.Errorf("failed to parse config: %w", err)
}
// Defaults
if opts.IntervalMS <= 0 {
opts.IntervalMS = DefaultRandomSourceIntervalMS
}
if opts.Format == "" {
opts.Format = DefaultRandomSourceFormat
}
if opts.Length <= 0 {
opts.Length = DefaultRandomSourceLength
}
// Validate
if opts.JitterMS < 0 {
return nil, fmt.Errorf("jitter_ms cannot be negative")
}
if opts.JitterMS > opts.IntervalMS {
opts.JitterMS = opts.IntervalMS
}
validateFormat := lconfig.OneOf("raw", "txt", "json")
if err := validateFormat(opts.Format); err != nil {
return nil, fmt.Errorf("format: %w", err)
opts, err := config.Decode[config.RandomSourceOptions]("source", "random", configMap)
if err != nil {
return nil, err
}
rs := &RandomSource{
+5 -23
View File
@@ -23,7 +23,6 @@ import (
"github.com/lixenwraith/logwisp/internal/source"
"github.com/lixenwraith/logwisp/internal/tlsx"
lconfig "github.com/lixenwraith/config"
"github.com/lixenwraith/log"
)
@@ -33,11 +32,6 @@ func init() {
}
}
const (
DefaultChainSourceBufferSize = 1000
DefaultChainSourceHelloTimeoutMS = 10000
)
// TCPChainSource accepts connections from upstream tcp_chain sinks and ingests NDJSON entries
type TCPChainSource struct {
id string
@@ -79,25 +73,13 @@ func NewTCPChainSourcePlugin(
logger *log.Logger,
proxy *session.Proxy,
) (source.Source, error) {
opts := &config.TCPChainSourceOptions{
Host: "0.0.0.0",
TrustNode: true,
}
if err := config.Scan(configMap, opts); err != nil {
return nil, fmt.Errorf("failed to parse config: %w", err)
}
if err := lconfig.Port(opts.Port); err != nil {
return nil, fmt.Errorf("port: %w", err)
opts, err := config.Decode[config.TCPChainSourceOptions]("source", "tcp_chain", configMap)
if err != nil {
return nil, err
}
network, err := core.Network(opts.Host)
if err != nil {
return nil, fmt.Errorf("host: %w", err)
}
if opts.BufferSize <= 0 {
opts.BufferSize = DefaultChainSourceBufferSize
}
if opts.HelloTimeoutMS <= 0 {
opts.HelloTimeoutMS = DefaultChainSourceHelloTimeoutMS
return nil, err
}
tlsCfg, err := tlsx.Server(opts.TLS, opts.Host)
if err != nil {
@@ -107,7 +89,7 @@ func NewTCPChainSourcePlugin(
if err != nil {
return nil, err
}
authPolicy, err := authz.New(opts.Auth, tlsCfg, aclPolicy, authz.RoleChainListener, authz.TCP)
authPolicy, err := authz.New(opts.Auth, tlsCfg, aclPolicy, config.ChainListener, authz.TCP)
if err != nil {
return nil, err
}