Database Package (internal/database/): - Database interface abstraction for multiple backends - SQLite implementation with pure Go driver (no CGO) - PostgreSQL implementation with connection pooling - Factory pattern for creating database from config - Tiered retention with automatic aggregation: - Raw metrics: 24h (5s resolution) - 1-minute aggregation: 7 days - 5-minute aggregation: 30 days - Hourly aggregation: 1 year Schema includes: - agents: registration, status, certificates - users: local + LDAP authentication - roles: RBAC with permissions JSON - sessions: token-based authentication - metrics_*: time-series with aggregation - alerts: triggered alerts with acknowledgment Configuration Updates: - DatabaseConfig with SQLite path and PostgreSQL settings - RetentionConfig for customizing data retention - Environment variables: TYTO_DB_*, TYTO_DB_CONNECTION_STRING - Default SQLite at /var/lib/tyto/tyto.db 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
354 lines
9.0 KiB
Go
354 lines
9.0 KiB
Go
package config
|
|
|
|
import (
|
|
"os"
|
|
"time"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
// Mode determines the operational mode of Tyto.
|
|
type Mode string
|
|
|
|
const (
|
|
// ModeStandalone is the default single-host monitoring mode.
|
|
// No database, no agents, minimal configuration.
|
|
ModeStandalone Mode = "standalone"
|
|
|
|
// ModeServer is the full multi-device monitoring mode.
|
|
// Requires database, supports agents, authentication, and RBAC.
|
|
ModeServer Mode = "server"
|
|
|
|
// ModeAgent runs as a lightweight agent that reports to a server.
|
|
ModeAgent Mode = "agent"
|
|
)
|
|
|
|
type Config struct {
|
|
// Mode determines standalone vs server operation
|
|
Mode Mode `yaml:"mode"`
|
|
|
|
// Server settings (HTTP API)
|
|
Port string `yaml:"port"`
|
|
RefreshInterval time.Duration `yaml:"-"`
|
|
RefreshSeconds int `yaml:"refresh_interval"`
|
|
|
|
// Paths for containerized access
|
|
ProcPath string `yaml:"proc_path"`
|
|
SysPath string `yaml:"sys_path"`
|
|
MtabPath string `yaml:"mtab_path"`
|
|
DockerSock string `yaml:"docker_socket"`
|
|
|
|
// Legacy authentication (standalone mode only)
|
|
AuthEnabled bool `yaml:"auth_enabled"`
|
|
AuthUser string `yaml:"auth_user"`
|
|
AuthPass string `yaml:"auth_pass"`
|
|
|
|
// TLS for HTTP server
|
|
TLSEnabled bool `yaml:"tls_enabled"`
|
|
TLSCertFile string `yaml:"tls_cert_file"`
|
|
TLSKeyFile string `yaml:"tls_key_file"`
|
|
|
|
// Alerts
|
|
Alerts AlertConfig `yaml:"alerts"`
|
|
|
|
// Server mode configuration
|
|
Server ServerConfig `yaml:"server"`
|
|
|
|
// Agent mode configuration
|
|
Agent AgentConfig `yaml:"agent"`
|
|
|
|
// Database configuration (server mode only)
|
|
Database DatabaseConfig `yaml:"database"`
|
|
}
|
|
|
|
// ServerConfig contains settings for server mode.
|
|
type ServerConfig struct {
|
|
// GRPCPort for agent connections
|
|
GRPCPort int `yaml:"grpc_port"`
|
|
|
|
// TLS settings for gRPC
|
|
TLS TLSConfig `yaml:"tls"`
|
|
|
|
// Registration settings
|
|
Registration RegistrationConfig `yaml:"registration"`
|
|
}
|
|
|
|
// TLSConfig contains mTLS settings.
|
|
type TLSConfig struct {
|
|
CACert string `yaml:"ca_cert"`
|
|
ServerCert string `yaml:"server_cert"`
|
|
ServerKey string `yaml:"server_key"`
|
|
}
|
|
|
|
// RegistrationConfig controls agent registration behavior.
|
|
type RegistrationConfig struct {
|
|
AutoEnabled bool `yaml:"auto_enabled"`
|
|
RequireApproval bool `yaml:"require_approval"`
|
|
}
|
|
|
|
// AgentConfig contains settings for agent mode.
|
|
type AgentConfig struct {
|
|
// ID uniquely identifies this agent
|
|
ID string `yaml:"id"`
|
|
|
|
// ServerURL is the address of the central server
|
|
ServerURL string `yaml:"server_url"`
|
|
|
|
// Interval between metric collections
|
|
Interval time.Duration `yaml:"-"`
|
|
IntervalSeconds int `yaml:"interval"`
|
|
|
|
// TLS settings for connecting to server
|
|
TLS AgentTLSConfig `yaml:"tls"`
|
|
}
|
|
|
|
// AgentTLSConfig contains agent-side TLS settings.
|
|
type AgentTLSConfig struct {
|
|
CACert string `yaml:"ca_cert"`
|
|
AgentCert string `yaml:"agent_cert"`
|
|
AgentKey string `yaml:"agent_key"`
|
|
}
|
|
|
|
// DatabaseConfig contains database connection settings.
|
|
type DatabaseConfig struct {
|
|
// Type is "sqlite" or "postgres"
|
|
Type string `yaml:"type"`
|
|
|
|
// SQLite settings
|
|
Path string `yaml:"path"` // Path to SQLite database file
|
|
|
|
// PostgreSQL settings
|
|
Host string `yaml:"host"`
|
|
Port int `yaml:"port"`
|
|
User string `yaml:"user"`
|
|
Password string `yaml:"password"`
|
|
Database string `yaml:"database"`
|
|
SSLMode string `yaml:"sslmode"`
|
|
ConnectionString string `yaml:"connection_string"` // Full connection string (overrides individual fields)
|
|
|
|
// Retention settings
|
|
Retention RetentionConfig `yaml:"retention"`
|
|
}
|
|
|
|
// RetentionConfig defines data retention policies.
|
|
type RetentionConfig struct {
|
|
RawRetention time.Duration `yaml:"raw"` // Raw metrics retention (default: 24h)
|
|
OneMinuteRetention time.Duration `yaml:"one_minute"` // 1-minute aggregation (default: 7d)
|
|
FiveMinuteRetention time.Duration `yaml:"five_minute"` // 5-minute aggregation (default: 30d)
|
|
HourlyRetention time.Duration `yaml:"hourly"` // Hourly aggregation (default: 1y)
|
|
}
|
|
|
|
type AlertConfig struct {
|
|
Enabled bool `yaml:"enabled"`
|
|
CPUThreshold float64 `yaml:"cpu_threshold"`
|
|
MemoryThreshold float64 `yaml:"memory_threshold"`
|
|
DiskThreshold float64 `yaml:"disk_threshold"`
|
|
TempThreshold float64 `yaml:"temp_threshold"`
|
|
}
|
|
|
|
func Load() *Config {
|
|
cfg := &Config{
|
|
Mode: ModeStandalone,
|
|
Port: "8080",
|
|
RefreshSeconds: 5,
|
|
ProcPath: "/proc",
|
|
SysPath: "/sys",
|
|
MtabPath: "/etc/mtab",
|
|
DockerSock: "/var/run/docker.sock",
|
|
Alerts: AlertConfig{
|
|
CPUThreshold: 90.0,
|
|
MemoryThreshold: 90.0,
|
|
DiskThreshold: 90.0,
|
|
TempThreshold: 80.0,
|
|
},
|
|
Server: ServerConfig{
|
|
GRPCPort: 9849,
|
|
Registration: RegistrationConfig{
|
|
AutoEnabled: true,
|
|
RequireApproval: true,
|
|
},
|
|
},
|
|
Agent: AgentConfig{
|
|
IntervalSeconds: 5,
|
|
},
|
|
Database: DatabaseConfig{
|
|
Type: "sqlite",
|
|
Path: "/var/lib/tyto/tyto.db",
|
|
Port: 5432,
|
|
SSLMode: "disable",
|
|
},
|
|
}
|
|
|
|
// Try to load from YAML config file
|
|
configPath := getEnv("TYTO_CONFIG", getEnv("CONFIG_FILE", "/etc/tyto/config.yaml"))
|
|
if data, err := os.ReadFile(configPath); err == nil {
|
|
yaml.Unmarshal(data, cfg)
|
|
}
|
|
|
|
// Environment variables override YAML
|
|
if val := os.Getenv("TYTO_MODE"); val != "" {
|
|
cfg.Mode = Mode(val)
|
|
}
|
|
if val := os.Getenv("PORT"); val != "" {
|
|
cfg.Port = val
|
|
}
|
|
if val := os.Getenv("PROC_PATH"); val != "" {
|
|
cfg.ProcPath = val
|
|
}
|
|
if val := os.Getenv("SYS_PATH"); val != "" {
|
|
cfg.SysPath = val
|
|
}
|
|
if val := os.Getenv("MTAB_PATH"); val != "" {
|
|
cfg.MtabPath = val
|
|
}
|
|
if val := os.Getenv("DOCKER_SOCKET"); val != "" {
|
|
cfg.DockerSock = val
|
|
}
|
|
if val := os.Getenv("AUTH_ENABLED"); val == "true" {
|
|
cfg.AuthEnabled = true
|
|
}
|
|
if val := os.Getenv("AUTH_USER"); val != "" {
|
|
cfg.AuthUser = val
|
|
}
|
|
if val := os.Getenv("AUTH_PASS"); val != "" {
|
|
cfg.AuthPass = val
|
|
}
|
|
if val := os.Getenv("TLS_ENABLED"); val == "true" {
|
|
cfg.TLSEnabled = true
|
|
}
|
|
if val := os.Getenv("TLS_CERT_FILE"); val != "" {
|
|
cfg.TLSCertFile = val
|
|
}
|
|
if val := os.Getenv("TLS_KEY_FILE"); val != "" {
|
|
cfg.TLSKeyFile = val
|
|
}
|
|
|
|
// Database environment variables
|
|
if val := os.Getenv("TYTO_DB_TYPE"); val != "" {
|
|
cfg.Database.Type = val
|
|
}
|
|
if val := os.Getenv("TYTO_DB_PATH"); val != "" {
|
|
cfg.Database.Path = val
|
|
}
|
|
if val := os.Getenv("TYTO_DB_HOST"); val != "" {
|
|
cfg.Database.Host = val
|
|
}
|
|
if val := os.Getenv("TYTO_DB_USER"); val != "" {
|
|
cfg.Database.User = val
|
|
}
|
|
if val := os.Getenv("TYTO_DB_PASSWORD"); val != "" {
|
|
cfg.Database.Password = val
|
|
}
|
|
if val := os.Getenv("TYTO_DB_NAME"); val != "" {
|
|
cfg.Database.Database = val
|
|
}
|
|
if val := os.Getenv("TYTO_DB_CONNECTION_STRING"); val != "" {
|
|
cfg.Database.ConnectionString = val
|
|
}
|
|
|
|
// Agent configuration
|
|
if val := os.Getenv("TYTO_AGENT_ID"); val != "" {
|
|
cfg.Agent.ID = val
|
|
}
|
|
if val := os.Getenv("TYTO_SERVER_URL"); val != "" {
|
|
cfg.Agent.ServerURL = val
|
|
}
|
|
|
|
// Parse intervals
|
|
if intervalStr := os.Getenv("DEFAULT_REFRESH_INTERVAL"); intervalStr != "" {
|
|
if d, err := time.ParseDuration(intervalStr); err == nil {
|
|
cfg.RefreshInterval = d
|
|
}
|
|
} else {
|
|
cfg.RefreshInterval = time.Duration(cfg.RefreshSeconds) * time.Second
|
|
}
|
|
|
|
cfg.Agent.Interval = time.Duration(cfg.Agent.IntervalSeconds) * time.Second
|
|
if cfg.Agent.Interval == 0 {
|
|
cfg.Agent.Interval = 5 * time.Second
|
|
}
|
|
|
|
return cfg
|
|
}
|
|
|
|
// DefaultConfig returns a config with default values.
|
|
func DefaultConfig() *Config {
|
|
return &Config{
|
|
Mode: ModeStandalone,
|
|
Port: "8080",
|
|
RefreshSeconds: 5,
|
|
ProcPath: "/proc",
|
|
SysPath: "/sys",
|
|
MtabPath: "/etc/mtab",
|
|
DockerSock: "/var/run/docker.sock",
|
|
Alerts: AlertConfig{
|
|
CPUThreshold: 90.0,
|
|
MemoryThreshold: 90.0,
|
|
DiskThreshold: 90.0,
|
|
TempThreshold: 80.0,
|
|
},
|
|
Server: ServerConfig{
|
|
GRPCPort: 9849,
|
|
Registration: RegistrationConfig{
|
|
AutoEnabled: true,
|
|
RequireApproval: true,
|
|
},
|
|
},
|
|
Agent: AgentConfig{
|
|
Interval: 5 * time.Second,
|
|
},
|
|
Database: DatabaseConfig{
|
|
Type: "sqlite",
|
|
Path: "/var/lib/tyto/tyto.db",
|
|
Port: 5432,
|
|
SSLMode: "disable",
|
|
},
|
|
RefreshInterval: 5 * time.Second,
|
|
}
|
|
}
|
|
|
|
// LoadFromPath loads configuration from a specific file path.
|
|
func LoadFromPath(path string) (*Config, error) {
|
|
cfg := DefaultConfig()
|
|
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := yaml.Unmarshal(data, cfg); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Parse intervals from seconds
|
|
cfg.RefreshInterval = time.Duration(cfg.RefreshSeconds) * time.Second
|
|
cfg.Agent.Interval = time.Duration(cfg.Agent.IntervalSeconds) * time.Second
|
|
if cfg.Agent.Interval == 0 {
|
|
cfg.Agent.Interval = 5 * time.Second
|
|
}
|
|
|
|
return cfg, nil
|
|
}
|
|
|
|
// IsStandalone returns true if running in standalone mode.
|
|
func (c *Config) IsStandalone() bool {
|
|
return c.Mode == ModeStandalone || c.Mode == ""
|
|
}
|
|
|
|
// IsServer returns true if running in server mode.
|
|
func (c *Config) IsServer() bool {
|
|
return c.Mode == ModeServer
|
|
}
|
|
|
|
// IsAgent returns true if running in agent mode.
|
|
func (c *Config) IsAgent() bool {
|
|
return c.Mode == ModeAgent
|
|
}
|
|
|
|
func getEnv(key, defaultVal string) string {
|
|
if val := os.Getenv(key); val != "" {
|
|
return val
|
|
}
|
|
return defaultVal
|
|
}
|