157 lines
4.4 KiB
Go
157 lines
4.4 KiB
Go
package config
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/spf13/cobra"
|
|
"github.com/spf13/viper"
|
|
)
|
|
|
|
type Config struct {
|
|
HetznerAPIKey string
|
|
NamecheapAPIKey string
|
|
NamecheapUser string
|
|
Domain string
|
|
Region string
|
|
ServerType string
|
|
DeploymentID string
|
|
ConfigDir string
|
|
LetsEncryptEmail string
|
|
}
|
|
|
|
func LoadConfig(cmd *cobra.Command) (*Config, error) {
|
|
return LoadConfigWithValidation(cmd, true)
|
|
}
|
|
|
|
func LoadConfigWithValidation(cmd *cobra.Command, validate bool) (*Config, error) {
|
|
homeDir, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get user home directory: %w", err)
|
|
}
|
|
|
|
configDir := filepath.Join(homeDir, ".sslh-lab")
|
|
configFile := filepath.Join(configDir, "config.yaml")
|
|
|
|
viper.SetConfigType("yaml")
|
|
viper.SetConfigFile(configFile)
|
|
|
|
viper.SetEnvPrefix("SSLH")
|
|
viper.AutomaticEnv()
|
|
|
|
viper.SetDefault("region", "nbg1")
|
|
viper.SetDefault("server_type", "cpx22")
|
|
viper.SetDefault("config_dir", configDir)
|
|
|
|
if err := viper.ReadInConfig(); err != nil {
|
|
var configFileNotFoundError viper.ConfigFileNotFoundError
|
|
var pathError *os.PathError
|
|
errMsg := strings.ToLower(err.Error())
|
|
|
|
if errors.As(err, &configFileNotFoundError) {
|
|
// Config file not found is OK - we'll use defaults/env vars/flags
|
|
} else if errors.As(err, &pathError) && os.IsNotExist(pathError) {
|
|
// File doesn't exist - this is OK
|
|
} else if os.IsNotExist(err) {
|
|
// Direct IsNotExist check
|
|
} else if strings.Contains(errMsg, "no such file") || strings.Contains(errMsg, "not found") {
|
|
// File not found error (handled as OK)
|
|
} else {
|
|
// Any other error is a real problem
|
|
return nil, fmt.Errorf("failed to read config file: %w", err)
|
|
}
|
|
}
|
|
|
|
config := &Config{
|
|
HetznerAPIKey: getStringValue(cmd, "hetzner-key", "HETZNER_KEY"),
|
|
NamecheapAPIKey: getStringValue(cmd, "namecheap-key", "NAMECHEAP_KEY"),
|
|
NamecheapUser: getStringValue(cmd, "namecheap-user", "NAMECHEAP_USER"),
|
|
Domain: getStringValue(cmd, "domain", "DOMAIN"),
|
|
Region: getStringValue(cmd, "region", "REGION"),
|
|
ServerType: getStringValue(cmd, "server-type", "SERVER_TYPE"),
|
|
ConfigDir: viper.GetString("config_dir"),
|
|
LetsEncryptEmail: getStringValue(cmd, "letsencrypt-email", "LETSENCRYPT_EMAIL"),
|
|
}
|
|
|
|
if validate {
|
|
if err := config.Validate(); err != nil {
|
|
return nil, fmt.Errorf("configuration validation failed: %w", err)
|
|
}
|
|
}
|
|
|
|
return config, nil
|
|
}
|
|
|
|
func getStringValue(cmd *cobra.Command, flagName, envKey string) string {
|
|
if cmd != nil {
|
|
if flagValue, err := cmd.Flags().GetString(flagName); err == nil && flagValue != "" {
|
|
return flagValue
|
|
}
|
|
}
|
|
|
|
if envValue := viper.GetString(envKey); envValue != "" {
|
|
return envValue
|
|
}
|
|
|
|
// For viper config file, convert hyphen to underscore (viper uses underscores)
|
|
viperKey := strings.ReplaceAll(flagName, "-", "_")
|
|
return viper.GetString(viperKey)
|
|
}
|
|
|
|
func (c *Config) Validate() error {
|
|
if c.HetznerAPIKey == "" {
|
|
return fmt.Errorf("hetzner API key is required (set via --hetzner-key, SSLH_HETZNER_KEY, or config file)")
|
|
}
|
|
|
|
if c.NamecheapAPIKey == "" {
|
|
return fmt.Errorf("namecheap API key is required (set via --namecheap-key, SSLH_NAMECHEAP_KEY, or config file)")
|
|
}
|
|
|
|
if c.NamecheapUser == "" {
|
|
return fmt.Errorf("namecheap username is required (set via --namecheap-user, SSLH_NAMECHEAP_USER, or config file)")
|
|
}
|
|
|
|
if c.Domain == "" {
|
|
return fmt.Errorf("domain is required (set via --domain, SSLH_DOMAIN, or config file)")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *Config) ValidateForTeardown() error {
|
|
if c.HetznerAPIKey == "" {
|
|
return fmt.Errorf("hetzner API key is required (set via --hetzner-key, SSLH_HETZNER_KEY, or config file)")
|
|
}
|
|
|
|
if c.NamecheapAPIKey == "" {
|
|
return fmt.Errorf("namecheap API key is required (set via --namecheap-key, SSLH_NAMECHEAP_KEY, or config file)")
|
|
}
|
|
|
|
if c.NamecheapUser == "" {
|
|
return fmt.Errorf("namecheap username is required (set via --namecheap-user, SSLH_NAMECHEAP_USER, or config file)")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *Config) GetDeploymentDir() string {
|
|
if c.DeploymentID == "" {
|
|
return ""
|
|
}
|
|
return filepath.Join(c.ConfigDir, "deployments", c.DeploymentID)
|
|
}
|
|
|
|
func (c *Config) EnsureConfigDir() error {
|
|
return os.MkdirAll(c.ConfigDir, 0755)
|
|
}
|
|
|
|
func (c *Config) EnsureDeploymentDir() error {
|
|
if c.DeploymentID == "" {
|
|
return fmt.Errorf("deployment ID not set")
|
|
}
|
|
return os.MkdirAll(c.GetDeploymentDir(), 0700)
|
|
}
|