Initial code commit
This commit is contained in:
156
internal/config/config.go
Normal file
156
internal/config/config.go
Normal file
@@ -0,0 +1,156 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user