config

package
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 2, 2026 License: Apache-2.0 Imports: 22 Imported by: 0

Documentation

Overview

Package config provides configuration loading and validation for the nstance-server.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DeleteGroup

func DeleteGroup(ctx context.Context, loader *Loader, tenant, key string) error

DeleteGroup removes a dynamic group for a specific tenant. If group exists in static config, only the dynamic overrides are removed.

func GetGroups

func GetGroups(ctx context.Context, loader *Loader, tenant string) (map[string]GroupConfig, error)

GetGroups returns all groups for a tenant, merging static and dynamic configs. Returns a map of final merged group configurations (map key is the group key).

func HashInfraConfig

func HashInfraConfig(cfg MergedConfig) string

HashInfraConfig computes the infra config hash from merged config This covers immutable fields requiring VM replacement

func HashRuntimeConfig

func HashRuntimeConfig(cfg MergedConfig) string

HashRuntimeConfig computes the runtime config hash from merged config This covers vars and files (anything that can be pushed to existing instances)

func IntPtr

func IntPtr(i int) *int

IntPtr returns a pointer to an int value (helper for struct literals)

func RegisterCustomValidators

func RegisterCustomValidators(v *validator.Validate) error

RegisterCustomValidators registers custom validation functions

func UpsertGroup

func UpsertGroup(ctx context.Context, loader *Loader, tenant, key string, group GroupConfig) error

UpsertGroup creates or updates a group for a specific tenant. - If group exists in static config: validates only size/instanceType/vars are changed - If group exists in dynamic config: merges non-zero fields - If new group: validates all required fields are present, writes to dynamic config

Types

type AdvertiseConfig

type AdvertiseConfig struct {
	HealthAddr       string `json:"health_addr" validate:"required,addr"`
	ElectionAddr     string `json:"election_addr" validate:"required,addr"`
	RegistrationAddr string `json:"registration_addr" validate:"required,addr"`
	OperatorAddr     string `json:"operator_addr" validate:"required,addr"`
	AgentAddr        string `json:"agent_addr" validate:"required,addr"`
}

AdvertiseConfig defines server advertised addresses with per-service addresses

type BindConfig

type BindConfig struct {
	HealthAddr       string `json:"health_addr" validate:"required,addr"`
	ElectionAddr     string `json:"election_addr" validate:"required,addr"`
	RegistrationAddr string `json:"registration_addr" validate:"required,addr"`
	OperatorAddr     string `json:"operator_addr" validate:"required,addr"`
	AgentAddr        string `json:"agent_addr" validate:"required,addr"`
}

BindConfig defines server binding configuration with per-service addresses

type CertConfig

type CertConfig struct {
	Kind         string   `json:"kind" validate:"required,oneof=client server"`
	CN           *string  `json:"cn"`           // Common Name (supports templating)
	Organization []string `json:"organization"` // Organization (for client certs)
	DNS          []string `json:"dns"`          // DNS SANs (supports templating)
	IP           []string `json:"ip"`           // IP SANs (supports templating)
	Country      []string `json:"country"`      // Country (C)
	Province     []string `json:"province"`     // State/Province (ST)
	Locality     []string `json:"locality"`     // Locality (L)
	Street       []string `json:"street"`       // Street Address
	PostalCode   []string `json:"postal_code"`  // Postal Code
	TTL          int      `json:"ttl"`          // Certificate TTL in hours (default: 8760 = 1 year)
}

CertConfig defines certificate template configuration

type ClusterConfig

type ClusterConfig struct {
	ID             string                `json:"id" validate:"required"`
	Storage        *ClusterStorageConfig `json:"storage,omitempty"`
	Secrets        SecretsConfig         `json:"secrets" validate:"required"`
	LeaderElection LeaderElectionConfig  `json:"leader_election"`
}

ClusterConfig defines cluster-scoped configuration shared across all shards.

type ClusterStorageConfig

type ClusterStorageConfig struct {
	Provider string `json:"provider" validate:"required,oneof=s3 gcs file"`
	Bucket   string `json:"bucket"`
	Region   string `json:"region,omitempty"`
	Endpoint string `json:"endpoint,omitempty"` // Used for S3-compatible object stores (SeaweedFS, MinIO, Ceph RGW)
	Prefix   string `json:"prefix"`             // Default: "cluster/"
}

ClusterStorageConfig defines optional separate storage for cluster-scoped data. If not specified, the shard bucket is used with the "cluster/" prefix.

type Config

type Config struct {
	Cluster       ClusterConfig                     `json:"cluster" validate:"required"`
	Shard         ShardConfig                       `json:"shard" validate:"required"`
	LoadBalancers map[string]LoadBalancerConfig     `json:"load_balancers"`
	Images        map[string]ImageConfig            `json:"images"`
	Certificates  map[string]CertConfig             `json:"certificates"`
	Defaults      DefaultsConfig                    `json:"defaults"`
	Templates     map[string]TemplateConfig         `json:"templates" validate:"required"`
	Groups        map[string]map[string]GroupConfig `json:"groups"` // tenant -> group key -> config
}

Config represents the complete Nstance Server configuration

func ParseBytes

func ParseBytes(data []byte) (*Config, error)

ParseBytes parses and validates configuration from bytes

func ValidateFile

func ValidateFile(filePath string) (*Config, error)

ValidateFile validates a local configuration file

func (*Config) Clone

func (c *Config) Clone() (*Config, error)

Clone creates a deep copy of the configuration

func (*Config) GetMergedConfig

func (c *Config) GetMergedConfig(template string, groupConfig GroupConfig) (*MergedConfig, error)

GetMergedConfig resolves and merges configuration for a specific template/group combination

func (*Config) GetShardStoragePrefix

func (c *Config) GetShardStoragePrefix() string

GetShardStoragePrefix returns the shard storage prefix in the format "shard/{shard.id}/".

func (*Config) IsClusterLeaderElectionEnabled

func (c *Config) IsClusterLeaderElectionEnabled() bool

IsClusterLeaderElectionEnabled returns true if cluster leader election is enabled or not set.

func (*Config) IsShardLeaderElectionEnabled

func (c *Config) IsShardLeaderElectionEnabled() bool

IsShardLeaderElectionEnabled returns true if shard leader election is enabled or not set.

func (*Config) ResolveSubnetKey

func (c *Config) ResolveSubnetKey(key string) ([]string, error)

ResolveSubnetKey resolves a subnet pool ID to its provider subnet IDs.

func (*Config) SetDefaults

func (c *Config) SetDefaults()

SetDefaults sets default values for optional fields

func (*Config) Validate

func (c *Config) Validate() error

Validate validates the configuration using struct tags

func (*Config) ValidateDynamicSubnetKey

func (c *Config) ValidateDynamicSubnetKey(key string) error

ValidateDynamicSubnetKey validates that the given subnet pool ID is allowed for external requests (dynamic groups, per-instance overrides). Returns nil if allowed, error otherwise.

func (*Config) ValidateSubnetConfig

func (c *Config) ValidateSubnetConfig() error

ValidateSubnetConfig validates the subnet configuration at config load time. Returns an error if validation fails.

type ConfigMetadata

type ConfigMetadata struct {
	ETag         string    `json:"etag"`
	LastModified time.Time `json:"last_modified"`
	Size         int64     `json:"size"`
}

ConfigMetadata contains metadata about a configuration file

type DefaultsConfig

type DefaultsConfig struct {
	Args     map[string]interface{} `json:"args"`
	Vars     map[string]string      `json:"vars"`
	Userdata *UserdataConfig        `json:"userdata,omitempty"`
}

DefaultsConfig defines global defaults

type Duration

type Duration time.Duration

Duration is a wrapper around time.Duration that supports parsing duration strings in JSON

func (Duration) Duration

func (d Duration) Duration() time.Duration

func (Duration) MarshalJSON

func (d Duration) MarshalJSON() ([]byte, error)

func (*Duration) UnmarshalJSON

func (d *Duration) UnmarshalJSON(b []byte) error

type EncryptionKeyConfig

type EncryptionKeyConfig struct {
	Provider string                 `json:"provider" validate:"required,oneof=env file aws-secrets-manager gcp-secret-manager"`
	Options  map[string]interface{} `json:"options,omitempty"`          // Provider-specific options
	Source   string                 `json:"source" validate:"required"` // env var name, file path, secret name, or AWS secret ARN
}

EncryptionKeyConfig defines Encryption Key configuration

type ErrorExitJitterConfig

type ErrorExitJitterConfig struct {
	MinDelay Duration `json:"min_delay"` // Minimum delay before exit (default: 10s)
	MaxDelay Duration `json:"max_delay"` // Maximum delay before exit (default: 40s)
}

ErrorExitJitterConfig defines jittered delay timing before server exits on error

type ExpiryConfig

type ExpiryConfig struct {
	EligibleAge Duration `json:"eligible_age,omitempty"` // Age at which managed instances become eligible for opportunistic expiry
	ForcedAge   Duration `json:"forced_age,omitempty"`   // Age at which managed instances are expired immediately
	OndemandAge Duration `json:"ondemand_age,omitempty"` // Maximum age for on-demand instances before forced expiry
}

ExpiryConfig defines instance expiry configuration

type FileConfig

type FileConfig struct {
	Kind     string      `json:"kind" validate:"required,oneof=secret certificate env json string"`
	Source   string      `json:"source,omitempty"`   // For secrets: secret name
	Template interface{} `json:"template,omitempty"` // For certificates: string template name; for env/json/string: template content
	Key      *KeyConfig  `json:"key,omitempty"`      // For certificates: key reference
}

FileConfig defines file configuration (secrets, certificates, or templates)

type GarbageCollectionConfig

type GarbageCollectionConfig struct {
	Interval               Duration `json:"interval,omitempty"`                 // How often to run GC (e.g., "2m"); 0 disables GC
	RegistrationTimeout    Duration `json:"registration_timeout,omitempty"`     // How long to wait for instance registration before terminating as dangling (e.g., "5m")
	DeletedRecordRetention Duration `json:"deleted_record_retention,omitempty"` // How long to keep instance records after deletion (e.g., "30m"); 0 uses default (30m)
}

GarbageCollectionConfig defines garbage collection scheduling options

type GroupConfig

type GroupConfig struct {
	Template      string                 `json:"template,omitempty" validate:"required,alphanum"`
	Size          *int                   `json:"size,omitempty" validate:"omitempty,min=0"` // nil = inherit from static config
	InstanceType  string                 `json:"instance_type,omitempty"`
	SubnetPool    string                 `json:"subnet_pool,omitempty"`
	Vars          map[string]string      `json:"vars,omitempty"`
	Args          map[string]interface{} `json:"args,omitempty"`
	DrainTimeout  *Duration              `json:"drain_timeout,omitempty"`  // How long to wait for drain (nil = use server default, 0 = immediate deletion)
	LoadBalancers []string               `json:"load_balancers,omitempty"` // References to load balancer keys
}

GroupConfig defines group configuration

func GetGroup

func GetGroup(ctx context.Context, loader *Loader, tenant, key string) (*GroupConfig, error)

GetGroup retrieves a group by tenant and key, merging static and dynamic configs. Returns the final merged group configuration.

func (GroupConfig) GetSize

func (g GroupConfig) GetSize() int

GetSize returns the size value, defaulting to 0 if nil

type ImageConfig

type ImageConfig struct {
	Provider string        `json:"provider" validate:"required,oneof=aws oci"` // Cloud provider (aws, oci for Oracle Cloud)
	Filters  []ImageFilter `json:"filters" validate:"required,min=1"`          // Image filters
	Owners   []string      `json:"owners,omitempty"`                           // Image owners (AWS account IDs, "self", or "amazon")
	Sort     string        `json:"sort" validate:"required"`                   // Field to sort by (e.g., "creation-date", "name")
	Order    string        `json:"order" validate:"required,oneof=asc desc"`   // Sort order (asc, desc)
	Fallback *string       `json:"fallback,omitempty"`                         // Optional fallback image ID
}

ImageConfig defines configuration for automatic image lookup

type ImageFilter

type ImageFilter struct {
	Name   string   `json:"name" validate:"required"`   // Filter name (e.g., "name", "owner-id")
	Values []string `json:"values" validate:"required"` // Filter values
}

ImageFilter defines a filter for image lookup

type InfraConfig

type InfraConfig struct {
	Provider string                 `json:"provider" validate:"required,oneof=aws gcp mock tmux proxmox"`
	Region   string                 `json:"region" validate:"required"`
	Zone     string                 `json:"zone" validate:"required"`
	Options  map[string]interface{} `json:"options,omitempty"` // Provider-specific options
}

InfraConfig defines infrastructure provider configuration for the shard.

type InstanceConfig

type InstanceConfig struct {
	Group        string            `json:"group" validate:"required,alphanum"` // Required group reference
	Template     string            `json:"template"`                           // Optional template override
	InstanceType string            `json:"instance_type"`                      // Optional instance type override
	SubnetPool   string            `json:"subnet_pool"`                        // Optional subnet pool override
	Vars         map[string]string `json:"vars"`                               // Additional vars
}

InstanceConfig defines individual instance configuration (for on-demand instances)

type KeyConfig

type KeyConfig struct {
	Source string `json:"source" validate:"required,oneof=agent"` // Where the key comes from
	Name   string `json:"name" validate:"required"`               // Name of the key
}

KeyConfig defines key reference configuration for certificates

type LeaderElectionConfig

type LeaderElectionConfig struct {
	Enabled            *bool    `json:"enabled,omitempty"`             // Default: true. Use pointer to distinguish unset from false
	FrequentInterval   Duration `json:"frequent_interval,omitempty"`   // Polling interval during transitions (default: 5s)
	InfrequentInterval Duration `json:"infrequent_interval,omitempty"` // Polling interval during stable periods (default: 30s)
	LeaderTimeout      Duration `json:"leader_timeout,omitempty"`      // Time before considering leader failed (default: 15s)
}

LeaderElectionConfig defines leader election timing options. Used for both cluster and shard leader election.

type LeaderNetworkConfig

type LeaderNetworkConfig struct {
	IP          string `json:"ip,omitempty"`           // Stable leader IP address (ENI private IP for AWS, reserved IP for GCP)
	InterfaceID string `json:"interface_id,omitempty"` // Provider resource ID: ENI ID for AWS; empty for GCP (which uses alias IP)
}

LeaderNetworkConfig defines stable leader network configuration for shard leadership. This enables a stable IP address to be assigned when acquiring leadership.

type LoadBalancerConfig

type LoadBalancerConfig struct {
	Provider string `json:"provider" validate:"required,oneof=aws gcp"`

	// AWS-specific fields
	TargetGroupArns []string `json:"target_group_arns,omitempty"` // Multiple ARNs for multi-port NLBs

	// GCP-specific fields
	BackendServiceName *string `json:"backend_service_name,omitempty"`
	InstanceGroupName  *string `json:"instance_group_name,omitempty"`
}

LoadBalancerConfig defines load balancer configuration

type Loader

type Loader struct {
	// contains filtered or unexported fields
}

Loader handles loading and caching of configuration from storage.

The loader uses a two-tier caching strategy:

  1. In-memory cache: fastest access for runtime operations
  2. Disk cache: persists across process restarts to minimize S3 reads

The disk cache is critical for cost control - if the server enters a crash loop or restarts frequently, it prevents excessive S3 GET requests which could become expensive at scale. On startup, we first check the disk cache before hitting S3.

The loader expects to receive shard-scoped storage - all paths are relative to the shard scope (e.g., "config.jsonc" not "shard/{shard}/config.jsonc").

func NewLoader

func NewLoader(opts LoaderOptions) (*Loader, error)

NewLoader creates a new configuration loader. Storage and CacheStorage should be scoped to the shard - the loader uses relative paths like "config.jsonc" and "groups.jsonc".

func (*Loader) CleanCache

func (l *Loader) CleanCache(ctx context.Context) error

CleanCache removes all cached files (config and groups)

func (*Loader) DeleteDynamicGroup

func (l *Loader) DeleteDynamicGroup(ctx context.Context, tenant, key string) error

DeleteDynamicGroup atomically removes a dynamic group and persists to S3.

func (*Loader) GetCurrent

func (l *Loader) GetCurrent() *Config

GetCurrent returns the currently loaded configuration

func (*Loader) GetDynamicGroup

func (l *Loader) GetDynamicGroup(tenant, key string) (GroupConfig, bool)

GetDynamicGroup returns a dynamic group from the in-memory cache for a specific tenant Returns empty config and false if not found, not loaded, or tenant is empty This method never triggers disk or S3 reads

func (*Loader) GetMetadata

func (l *Loader) GetMetadata() *ConfigMetadata

GetMetadata returns the metadata of the currently loaded configuration

func (*Loader) LoadConfigAndGroups

func (l *Loader) LoadConfigAndGroups(ctx context.Context, forceRefresh bool) (*Config, error)

LoadConfigAndGroups loads the configuration and dynamic groups from storage. If forceRefresh is false, it will use local cache if available (faster startup). If forceRefresh is true, it always downloads from S3 (for explicit refresh). After loading, it syncs all groups to SQLite for runtime operations.

func (*Loader) LoadDynamicGroups

func (l *Loader) LoadDynamicGroups(ctx context.Context) (map[string]map[string]GroupConfig, error)

LoadDynamicGroups loads dynamic groups from S3 groups.jsonc. Returns the cached map for read-only access. Do not mutate the returned map; use SetDynamicGroup/DeleteDynamicGroup for mutations.

func (*Loader) RefreshGroups

func (l *Loader) RefreshGroups(ctx context.Context) error

RefreshGroups forces a refresh of the dynamic groups from storage

func (*Loader) SetConfig

func (l *Loader) SetConfig(config *Config)

SetConfig directly sets the configuration (for testing only)

func (*Loader) SetDynamicGroup

func (l *Loader) SetDynamicGroup(ctx context.Context, tenant, key string, group GroupConfig) error

SetDynamicGroup atomically updates a single dynamic group and persists to S3. This is the only safe way to mutate dynamic groups. Uses ETag from disk cache to prevent concurrent modification. Updates both in-memory and disk cache on successful write.

func (*Loader) SyncGroupsToCache

func (l *Loader) SyncGroupsToCache(ctx context.Context) error

SyncGroupsToCache syncs all effective groups to SQLite with computed hashes. This should be called after config or groups are loaded/reloaded. Groups cannot exist in SQLite without hashes - they are computed and stored atomically.

type LoaderOptions

type LoaderOptions struct {
	Storage       storage.Storage // Main storage (e.g. S3), scoped to shard
	CacheStorage  storage.Storage // Cache storage (e.g. local filesystem), scoped to shard
	LocalDB       *localdb.DB     // Local SQLite database for caching groups with hashes
	Logger        *slog.Logger
	MaxRetries    *int   // Maximum retry attempts (nil = use default)
	AdvertiseHost string // CLI override for advertise health/election host (empty = use auto-detection)
}

LoaderOptions contains options for creating a new Loader

type MergedConfig

type MergedConfig struct {
	Args         map[string]interface{} `json:"args"`
	Vars         map[string]string      `json:"vars"`
	Files        map[string]FileConfig  `json:"files"`
	Userdata     *UserdataConfig        `json:"userdata,omitempty"`
	InstanceType string                 `json:"instance_type"`
	SubnetPool   string                 `json:"subnet_pool"`
	Kind         string                 `json:"kind"`
	Arch         string                 `json:"arch"`
}

MergedConfig represents a fully resolved configuration after merging

type SecretsConfig

type SecretsConfig struct {
	Provider          string                `json:"provider" validate:"required,oneof=object-storage aws-secrets-manager gcp-secret-manager memory"`
	Prefix            string                `json:"prefix,omitempty"`              // Prefix for secrets (S3 path or AWS name prefix)
	EncryptionKey     *EncryptionKeyConfig  `json:"encryption_key,omitempty"`      // Current key (used for encryption)
	OldEncryptionKeys []EncryptionKeyConfig `json:"old_encryption_keys,omitempty"` // Old keys (decryption only during rotation)
	CacheTTL          Duration              `json:"cache_ttl,omitempty"`           // Cache TTL for secrets, 0 disables caching
}

SecretsConfig defines secrets management configuration

type ShardConfig

type ShardConfig struct {
	ID                   string                  `json:"id" validate:"required"`
	Infra                InfraConfig             `json:"infra" validate:"required"`
	Bind                 BindConfig              `json:"bind" validate:"required"`
	Advertise            AdvertiseConfig         `json:"advertise" validate:"required"`
	LeaderNetwork        *LeaderNetworkConfig    `json:"leader_network,omitempty"`
	RequestTimeout       Duration                `json:"request_timeout,omitempty"`
	CreateRateLimit      Duration                `json:"create_rate_limit,omitempty"`
	HealthCheckInterval  Duration                `json:"health_check_interval,omitempty"`
	DefaultDrainTimeout  Duration                `json:"default_drain_timeout,omitempty"`
	ImageRefreshInterval Duration                `json:"image_refresh_interval,omitempty"`
	ShutdownTimeout      Duration                `json:"shutdown_timeout,omitempty"`
	SubnetPools          map[string][]string     `json:"subnet_pools"`
	DynamicSubnetPools   []string                `json:"dynamic_subnet_pools"`
	GarbageCollection    GarbageCollectionConfig `json:"garbage_collection"`
	Expiry               ExpiryConfig            `json:"expiry"`
	LeaderElection       LeaderElectionConfig    `json:"leader_election"`
	ErrorExitJitter      ErrorExitJitterConfig   `json:"error_exit_jitter"`
}

ShardConfig defines shard-specific configuration for a single shard server.

type TemplateConfig

type TemplateConfig struct {
	Kind         string                 `json:"kind" validate:"required,len=3,lowercase,alpha"`
	Arch         string                 `json:"arch" validate:"required,oneof=amd64 arm64"`
	Files        map[string]FileConfig  `json:"files"` // fileName -> FileConfig mapping
	Args         map[string]interface{} `json:"args"`
	Userdata     *UserdataConfig        `json:"userdata,omitempty"`
	Size         int                    `json:"size" validate:"min=0"`
	InstanceType string                 `json:"instance_type"`
	SubnetPool   string                 `json:"subnet_pool"`
	Vars         map[string]string      `json:"vars"`
}

TemplateConfig defines instance template configuration

type TenantGroup

type TenantGroup struct {
	Tenant string
	Key    string
	Config GroupConfig
}

TenantGroup represents a group with its tenant context

func GetAllGroups

func GetAllGroups(ctx context.Context, loader *Loader) ([]TenantGroup, error)

GetAllGroups returns all groups across all tenants, merging static and dynamic configs Returns a slice of TenantGroup containing tenant, group key, and merged config

type UserdataConfig

type UserdataConfig struct {
	Source   string `json:"source,omitempty"`   // "inline" (default) or "url"
	Encoding string `json:"encoding,omitempty"` // "plain" (default), "base64", "gzip", "base64+gzip"
	Content  string `json:"content" validate:"required"`
}

UserdataConfig defines userdata configuration for instance templates

func (*UserdataConfig) Validate

func (u *UserdataConfig) Validate() error

Validate validates the UserdataConfig

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL