Files
at-container-registry/scanner/internal/config/config.go
T

125 lines
4.3 KiB
Go

// Package config provides Viper-based configuration for the scanner service.
package config
import (
"fmt"
"github.com/spf13/viper"
"atcr.io/pkg/config"
)
// Config holds all scanner configuration.
type Config struct {
Version string `yaml:"version" comment:"Configuration format version."`
LogLevel string `yaml:"log_level" comment:"Log level: debug, info, warn, error."`
LogShipper config.LogShipperConfig `yaml:"log_shipper" comment:"Remote log shipping settings."`
Server ServerConfig `yaml:"server" comment:"Health endpoint settings."`
Hold HoldConfig `yaml:"hold" comment:"Hold service connection settings."`
Scanner ScannerConfig `yaml:"scanner" comment:"Worker pool settings."`
Vuln VulnConfig `yaml:"vuln" comment:"Vulnerability scanning (Grype) settings."`
}
// ServerConfig defines the health endpoint settings.
type ServerConfig struct {
// Listen address for the health endpoint.
Addr string `yaml:"addr" comment:"Listen address for the health endpoint, e.g. \":9090\"."`
}
// HoldConfig defines the hold service connection.
type HoldConfig struct {
// WebSocket URL of the hold service.
URL string `yaml:"url" comment:"WebSocket URL of the hold service (REQUIRED), e.g. \"ws://localhost:8080\"."`
// Shared secret for scanner authentication.
Secret string `yaml:"secret" comment:"Shared secret for scanner WebSocket auth (REQUIRED)."`
}
// ScannerConfig defines worker pool settings.
type ScannerConfig struct {
// Number of concurrent scan workers.
Workers int `yaml:"workers" comment:"Number of concurrent scan workers."`
// Maximum priority queue depth.
QueueSize int `yaml:"queue_size" comment:"Maximum priority queue depth."`
}
// VulnConfig defines vulnerability scanning settings.
type VulnConfig struct {
// Enable Grype vulnerability scanning.
Enabled bool `yaml:"enabled" comment:"Enable Grype vulnerability scanning."`
// Directory for the Grype vulnerability database.
DBPath string `yaml:"db_path" comment:"Directory for the Grype vulnerability database."`
// Directory for temporary layer extraction.
TmpDir string `yaml:"tmp_dir" comment:"Directory for temporary layer extraction."`
// Maximum total compressed image size in bytes. Images exceeding this are skipped. 0 = no limit.
MaxImageSize int64 `yaml:"max_image_size" comment:"Maximum total compressed image size in bytes. 0 = no limit. Default: 2 GiB."`
}
// setScannerDefaults registers all default values on the given Viper instance.
func setScannerDefaults(v *viper.Viper) {
v.SetDefault("version", "0.1")
v.SetDefault("log_level", "info")
// Server defaults
v.SetDefault("server.addr", ":9090")
// Hold defaults
v.SetDefault("hold.url", "")
v.SetDefault("hold.secret", "")
// Scanner defaults
v.SetDefault("scanner.workers", 1)
v.SetDefault("scanner.queue_size", 100)
// Vuln defaults
v.SetDefault("vuln.enabled", true)
v.SetDefault("vuln.db_path", "/var/lib/atcr-scanner/vulndb")
v.SetDefault("vuln.tmp_dir", "/var/lib/atcr-scanner/tmp")
v.SetDefault("vuln.max_image_size", 2*1024*1024*1024) // 2 GiB
// Log shipper defaults
v.SetDefault("log_shipper.batch_size", 100)
v.SetDefault("log_shipper.flush_interval", "5s")
}
// DefaultConfig returns a Config populated with all default values (no validation).
func DefaultConfig() *Config {
v := config.NewViper("SCANNER", "")
setScannerDefaults(v)
cfg := &Config{}
_ = v.Unmarshal(cfg, config.UnmarshalOption())
return cfg
}
// ExampleYAML returns a fully-commented YAML configuration with default values.
func ExampleYAML() ([]byte, error) {
return config.MarshalCommentedYAML("ATCR Scanner Configuration", DefaultConfig())
}
// LoadConfig builds a complete configuration using Viper layered loading:
// defaults -> YAML file -> environment variables.
// yamlPath is optional; empty string means env-only (backward compatible).
func LoadConfig(yamlPath string) (*Config, error) {
v := config.NewViper("SCANNER", yamlPath)
setScannerDefaults(v)
cfg := &Config{}
if err := v.Unmarshal(cfg, config.UnmarshalOption()); err != nil {
return nil, fmt.Errorf("failed to unmarshal config: %w", err)
}
if cfg.Hold.URL == "" {
return nil, fmt.Errorf("hold.url is required (env: SCANNER_HOLD_URL)")
}
if cfg.Hold.Secret == "" {
return nil, fmt.Errorf("hold.secret is required (env: SCANNER_HOLD_SECRET)")
}
return cfg, nil
}