Skip to content

Commit e77315d

Browse files
committed
Parse config into struct
1 parent 18beff8 commit e77315d

3 files changed

Lines changed: 100 additions & 24 deletions

File tree

cmd/mpcium/main.go

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,9 @@ func runNode(ctx context.Context, c *cli.Command) error {
113113

114114
viper.SetDefault("backup_enabled", true)
115115
config.InitViperConfig(configPath)
116-
environment := viper.GetString("environment")
116+
117+
appConfig := config.LoadConfig()
118+
environment := appConfig.Environment
117119
logger.Init(environment, debug)
118120

119121
// Handle password file if provided
@@ -127,15 +129,15 @@ func runNode(ctx context.Context, c *cli.Command) error {
127129
promptForSensitiveCredentials()
128130
} else {
129131
// Validate the config values
130-
checkRequiredConfigValues()
132+
checkRequiredConfigValues(appConfig)
131133
}
132134

133135
consulClient := infra.GetConsulClient(environment)
134136
keyinfoStore := keyinfo.NewStore(consulClient.KV())
135137
peers := LoadPeersFromConsul(consulClient)
136138
nodeID := GetIDFromName(nodeName, peers)
137139

138-
badgerKV := NewBadgerKV(nodeName, nodeID)
140+
badgerKV := NewBadgerKV(nodeName, nodeID, appConfig)
139141
defer badgerKV.Close()
140142

141143
// Start background backup job
@@ -151,7 +153,7 @@ func runNode(ctx context.Context, c *cli.Command) error {
151153
logger.Fatal("Failed to create identity store", err)
152154
}
153155

154-
natsConn, err := GetNATSConnection(environment)
156+
natsConn, err := GetNATSConnection(environment, appConfig)
155157
if err != nil {
156158
logger.Fatal("Failed to connect to NATS", err)
157159
}
@@ -388,9 +390,9 @@ func maskString(s string) string {
388390
}
389391

390392
// Check required configuration values are present
391-
func checkRequiredConfigValues() {
393+
func checkRequiredConfigValues(appConfig *config.AppConfig) {
392394
// Show warning if we're using file-based config but no password is set
393-
if viper.GetString("badger_password") == "" {
395+
if appConfig.BadgerPassword == "" {
394396
logger.Fatal("Badger password is required", nil)
395397
}
396398

@@ -441,7 +443,7 @@ func GetIDFromName(name string, peers []config.Peer) string {
441443
return nodeID
442444
}
443445

444-
func NewBadgerKV(nodeName, nodeID string) *kvstore.BadgerKVStore {
446+
func NewBadgerKV(nodeName, nodeID string, appConfig *config.AppConfig) *kvstore.BadgerKVStore {
445447
// Badger KV DB
446448
// Use configured db_path or default to current directory + "db"
447449
basePath := viper.GetString("db_path")
@@ -459,8 +461,8 @@ func NewBadgerKV(nodeName, nodeID string) *kvstore.BadgerKVStore {
459461
// Create BadgerConfig struct
460462
config := kvstore.BadgerConfig{
461463
NodeID: nodeName,
462-
EncryptionKey: []byte(viper.GetString("badger_password")),
463-
BackupEncryptionKey: []byte(viper.GetString("badger_password")), // Using same key for backup encryption
464+
EncryptionKey: []byte(appConfig.BadgerPassword),
465+
BackupEncryptionKey: []byte(appConfig.BadgerPassword), // Using same key for backup encryption
464466
BackupDir: backupDir,
465467
DBPath: dbPath,
466468
}
@@ -499,8 +501,8 @@ func StartPeriodicBackup(ctx context.Context, badgerKV *kvstore.BadgerKVStore, p
499501
return backupCancel
500502
}
501503

502-
func GetNATSConnection(environment string) (*nats.Conn, error) {
503-
url := viper.GetString("nats.url")
504+
func GetNATSConnection(environment string, appConfig *config.AppConfig) (*nats.Conn, error) {
505+
url := appConfig.NATs.URL
504506
opts := []nats.Option{
505507
nats.MaxReconnects(-1), // retry forever
506508
nats.ReconnectWait(2 * time.Second),
@@ -517,9 +519,12 @@ func GetNATSConnection(environment string) (*nats.Conn, error) {
517519

518520
if environment == constant.EnvProduction {
519521
// Load TLS config from configuration
520-
clientCert := viper.GetString("nats.tls.client_cert")
521-
clientKey := viper.GetString("nats.tls.client_key")
522-
caCert := viper.GetString("nats.tls.ca_cert")
522+
var clientCert, clientKey, caCert string
523+
if appConfig.NATs.TLS != nil {
524+
clientCert = appConfig.NATs.TLS.ClientCert
525+
clientKey = appConfig.NATs.TLS.ClientKey
526+
caCert = appConfig.NATs.TLS.CACert
527+
}
523528

524529
// Fallback to default paths if not configured
525530
if clientCert == "" {
@@ -535,7 +540,7 @@ func GetNATSConnection(environment string) (*nats.Conn, error) {
535540
opts = append(opts,
536541
nats.ClientCert(clientCert, clientKey),
537542
nats.RootCAs(caCert),
538-
nats.UserInfo(viper.GetString("nats.username"), viper.GetString("nats.password")),
543+
nats.UserInfo(appConfig.NATs.Username, appConfig.NATs.Password),
539544
)
540545
}
541546

pkg/config/config_test.go

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ func TestAppConfig_MarshalJSONMask(t *testing.T) {
2626
masked := config.MarshalJSONMask()
2727

2828
// Verify that sensitive data is masked
29-
assert.Contains(t, masked, "localhost:8500") // Address should not be masked
30-
assert.Contains(t, masked, "admin") // Username should not be masked
31-
assert.Contains(t, masked, "nats_user") // Username should not be masked
29+
assert.Contains(t, masked, "localhost:8500") // Address should not be masked
30+
assert.Contains(t, masked, "admin") // Username should not be masked
31+
assert.Contains(t, masked, "nats_user") // Username should not be masked
3232
assert.Contains(t, masked, "nats://localhost:4222") // URL should not be masked
3333

3434
// Verify that passwords are masked
@@ -120,4 +120,58 @@ func TestAppConfig_PartialConfig(t *testing.T) {
120120
assert.Contains(t, masked, "localhost:8500")
121121
assert.NotContains(t, masked, "test")
122122
assert.Contains(t, masked, "****") // masked badger password
123-
}
123+
}
124+
125+
func TestValidateEnvironment(t *testing.T) {
126+
tests := []struct {
127+
name string
128+
environment string
129+
wantErr bool
130+
}{
131+
{
132+
name: "valid production environment",
133+
environment: "production",
134+
wantErr: false,
135+
},
136+
{
137+
name: "valid development environment",
138+
environment: "development",
139+
wantErr: false,
140+
},
141+
{
142+
name: "invalid environment",
143+
environment: "staging",
144+
wantErr: true,
145+
},
146+
{
147+
name: "empty environment",
148+
environment: "",
149+
wantErr: true,
150+
},
151+
{
152+
name: "case sensitive - Production",
153+
environment: "Production",
154+
wantErr: true,
155+
},
156+
{
157+
name: "case sensitive - PRODUCTION",
158+
environment: "PRODUCTION",
159+
wantErr: true,
160+
},
161+
}
162+
163+
for _, tt := range tests {
164+
t.Run(tt.name, func(t *testing.T) {
165+
err := validateEnvironment(tt.environment)
166+
if tt.wantErr {
167+
assert.Error(t, err)
168+
if err != nil {
169+
assert.Contains(t, err.Error(), "invalid environment")
170+
assert.Contains(t, err.Error(), "production, development")
171+
}
172+
} else {
173+
assert.NoError(t, err)
174+
}
175+
})
176+
}
177+
}

pkg/config/init.go

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package config
22

33
import (
44
"encoding/json"
5+
"fmt"
56
"log"
67
"strings"
78

@@ -14,6 +15,7 @@ type AppConfig struct {
1415
Consul *ConsulConfig `mapstructure:"consul"`
1516
NATs *NATsConfig `mapstructure:"nats"`
1617

18+
Environment string `mapstructure:"environment"`
1719
BadgerPassword string `mapstructure:"badger_password"`
1820
}
1921

@@ -58,13 +60,12 @@ func InitViperConfig(configPath string) {
5860
viper.SetConfigFile(configPath)
5961
} else {
6062
// Use default behavior - search for config.yaml in common locations
61-
viper.SetConfigName("config") // name of config file (without extension)
62-
viper.SetConfigType("yaml") // REQUIRED if the config file does not have the extension in the name
63-
viper.AddConfigPath(".") // optionally look for config in the working directory
64-
viper.AddConfigPath("/etc/mpcium/") // look for config in /etc/mpcium/
63+
viper.SetConfigName("config") // name of config file (without extension)
64+
viper.SetConfigType("yaml") // REQUIRED if the config file does not have the extension in the name
65+
viper.AddConfigPath(".") // optionally look for config in the working directory
66+
viper.AddConfigPath("/etc/mpcium/") // look for config in /etc/mpcium/
6567
viper.AddConfigPath("$HOME/.mpcium/") // look for config in home directory
6668
}
67-
6869
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
6970
viper.AutomaticEnv()
7071
err := viper.ReadInConfig() // Find and read the config file
@@ -96,5 +97,21 @@ func LoadConfig() *AppConfig {
9697
log.Fatal("Failed to decode config", err)
9798
}
9899

100+
if err := validateEnvironment(config.Environment); err != nil {
101+
log.Fatal("Config validation failed:", err)
102+
}
103+
99104
return &config
100105
}
106+
107+
func validateEnvironment(environment string) error {
108+
validEnvironments := []string{"production", "development"}
109+
110+
for _, validEnv := range validEnvironments {
111+
if environment == validEnv {
112+
return nil
113+
}
114+
}
115+
116+
return fmt.Errorf("invalid environment '%s'. Must be one of: %s", environment, strings.Join(validEnvironments, ", "))
117+
}

0 commit comments

Comments
 (0)