package settings import ( "errors" "fmt" "log/slog" "os" "path/filepath" "vctp/internal/utils" "gopkg.in/yaml.v2" ) type Settings struct { SettingsPath string Logger *slog.Logger Values *SettingsYML } // SettingsYML struct holds various runtime data that is too cumbersome to specify via command line, eg replacement properties type SettingsYML struct { Settings struct { LogLevel string `yaml:"log_level"` LogOutput string `yaml:"log_output"` DatabaseDriver string `yaml:"database_driver"` DatabaseURL string `yaml:"database_url"` BindIP string `yaml:"bind_ip"` BindPort int `yaml:"bind_port"` BindDisableTLS bool `yaml:"bind_disable_tls"` TLSCertFilename string `yaml:"tls_cert_filename"` TLSKeyFilename string `yaml:"tls_key_filename"` VcenterUsername string `yaml:"vcenter_username"` VcenterPassword string `yaml:"vcenter_password"` VcenterInsecure bool `yaml:"vcenter_insecure"` VcenterEventPollingSeconds int `yaml:"vcenter_event_polling_seconds"` VcenterInventoryPollingSeconds int `yaml:"vcenter_inventory_polling_seconds"` VcenterInventorySnapshotSeconds int `yaml:"vcenter_inventory_snapshot_seconds"` VcenterInventoryAggregateSeconds int `yaml:"vcenter_inventory_aggregate_seconds"` HourlySnapshotMaxAgeDays int `yaml:"hourly_snapshot_max_age_days"` DailySnapshotMaxAgeMonths int `yaml:"daily_snapshot_max_age_months"` SnapshotCleanupCron string `yaml:"snapshot_cleanup_cron"` TenantsToFilter []string `yaml:"tenants_to_filter"` NodeChargeClusters []string `yaml:"node_charge_clusters"` SrmActiveActiveVms []string `yaml:"srm_activeactive_vms"` VcenterAddresses []string `yaml:"vcenter_addresses"` } `yaml:"settings"` } func New(logger *slog.Logger, settingsPath string) *Settings { return &Settings{ SettingsPath: utils.GetFilePath(settingsPath), Logger: logger, } } func (s *Settings) ReadYMLSettings() error { // Create config structure var settings SettingsYML // Check for empty filename if len(s.SettingsPath) == 0 { return errors.New("settings file path not specified") } //path := utils.GetFilePath(settingsPath) // Open config file file, err := os.Open(s.SettingsPath) if err != nil { return fmt.Errorf("unable to open settings file : '%s'", err) } s.Logger.Debug("Opened settings yaml file", "file_path", s.SettingsPath) defer file.Close() // Init new YAML decode d := yaml.NewDecoder(file) // Start YAML decoding from file if err := d.Decode(&settings); err != nil { return fmt.Errorf("unable to decode settings file : '%s'", err) } s.Logger.Debug("Updating settings", "settings", settings) s.Values = &settings return nil } func (s *Settings) WriteYMLSettings() error { if s.Values == nil { return errors.New("settings are not loaded") } if len(s.SettingsPath) == 0 { return errors.New("settings file path not specified") } data, err := yaml.Marshal(s.Values) if err != nil { return fmt.Errorf("unable to encode settings file: %w", err) } mode := os.FileMode(0o644) if info, err := os.Stat(s.SettingsPath); err == nil { mode = info.Mode().Perm() } dir := filepath.Dir(s.SettingsPath) tmp, err := os.CreateTemp(dir, "vctp-settings-*.yml") if err != nil { return fmt.Errorf("unable to create temp settings file: %w", err) } tmpName := tmp.Name() defer func() { _ = os.Remove(tmpName) }() if _, err := tmp.Write(data); err != nil { _ = tmp.Close() return fmt.Errorf("unable to write temp settings file: %w", err) } if err := tmp.Chmod(mode); err != nil { _ = tmp.Close() return fmt.Errorf("unable to set temp settings permissions: %w", err) } if err := tmp.Close(); err != nil { return fmt.Errorf("unable to close temp settings file: %w", err) } if err := os.Rename(tmpName, s.SettingsPath); err != nil { return fmt.Errorf("unable to replace settings file: %w", err) } return nil }