Skip to content

Commit

Permalink
Fix bug that prevented default config file from being loaded.
Browse files Browse the repository at this point in the history
Tests for LoadConfig().
  • Loading branch information
DanielKrawisz committed Jun 2, 2016
1 parent bb52081 commit 13934c2
Show file tree
Hide file tree
Showing 4 changed files with 211 additions and 27 deletions.
2 changes: 1 addition & 1 deletion bmd.go
Expand Up @@ -39,7 +39,7 @@ var (
func bmdMain() error {

// Load configuration.
tcfg, _, err := loadConfig(false)
tcfg, _, err := loadConfig()
if err != nil {
return err
}
Expand Down
60 changes: 35 additions & 25 deletions config.go
Expand Up @@ -37,7 +37,7 @@ const (
defaultMaxRPCClients = 25
defaultDbType = "boltdb"
defaultPort = 8444 // 8444
defaultRPCPort = 8442
defaultRPCPort = 8442
defaultMaxUpPerPeer = 2 * 1024 * 1024 // 2MBps
defaultMaxDownPerPeer = 2 * 1024 * 1024 // 2MBps
defaultMaxOutbound = 10
Expand Down Expand Up @@ -345,8 +345,14 @@ func createDir(path, name string) error {
}

// newConfigParser returns a new command line flags parser.
func newConfigParser(cfg *config, options flags.Options) *flags.Parser {
return flags.NewParser(cfg, options)
func newConfigParser(cfg *config, appName string, options flags.Options) *flags.Parser {
p := flags.NewNamedParser(appName, options)

if cfg != nil {
p.AddGroup("Application Options", "", cfg)
}

return p
}

// loadConfig initializes and parses the config using a config file and command
Expand All @@ -362,7 +368,15 @@ func newConfigParser(cfg *config, options flags.Options) *flags.Parser {
// The above results in bmd functioning properly without any config settings
// while still allowing the user to override settings with config files and
// command line options. Command line options always take precedence.
func loadConfig(isTest bool) (*config, []string, error) {
func loadConfig() (*config, []string, error) {

appName := filepath.Base(os.Args[0])
appName = strings.TrimSuffix(appName, filepath.Ext(appName))

return LoadConfig(appName, os.Args[1:])
}

func LoadConfig(appName string, args []string) (*config, []string, error) {
// Default config.
cfg := config{
ConfigFile: defaultConfigFile,
Expand All @@ -389,20 +403,16 @@ func loadConfig(isTest bool) (*config, []string, error) {
// the final parse below.
preCfg := cfg
var err error
if !isTest {
preParser := newConfigParser(&preCfg, flags.HelpFlag)
_, err = preParser.Parse()
if err != nil {
if e, ok := err.(*flags.Error); ok && e.Type == flags.ErrHelp {
fmt.Fprintln(os.Stderr, err)
return nil, nil, err
}
preParser := newConfigParser(&preCfg, appName, flags.HelpFlag)
_, err = preParser.ParseArgs(args)
if err != nil {
if e, ok := err.(*flags.Error); ok && e.Type == flags.ErrHelp {
fmt.Fprintln(os.Stderr, err)
return nil, nil, err
}
}

// Show the version and exit if the version flag was specified.
appName := filepath.Base(os.Args[0])
appName = strings.TrimSuffix(appName, filepath.Ext(appName))
usageMessage := fmt.Sprintf("Use %s -h to show usage", appName)
if preCfg.ShowVersion {
fmt.Println(appName, "version", version())
Expand All @@ -411,10 +421,12 @@ func loadConfig(isTest bool) (*config, []string, error) {

// Load additional config from file.
var configFileError error
parser := newConfigParser(&cfg, flags.Default)
if preCfg.ConfigFile != defaultConfigFile {

parser := newConfigParser(&cfg, appName, flags.Default)
// If the default location is specified, then the file isn't required to exist.
if preCfg.ConfigFile != defaultConfigFile || fileExists(preCfg.ConfigFile) {

err = flags.NewIniParser(parser).ParseFile(preCfg.ConfigFile)

if err != nil {
if _, ok := err.(*os.PathError); !ok {
fmt.Fprintf(os.Stderr, "Error parsing config "+
Expand All @@ -426,16 +438,14 @@ func loadConfig(isTest bool) (*config, []string, error) {
}
}

// Parse command line options again to ensure they take precedence.
var remainingArgs []string
if !isTest {
// Parse command line options again to ensure they take precedence.
remainingArgs, err = parser.Parse()
if err != nil {
if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp {
fmt.Fprintln(os.Stderr, usageMessage)
}
return nil, nil, err
remainingArgs, err = parser.ParseArgs(args)
if err != nil {
if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp {
fmt.Fprintln(os.Stderr, usageMessage)
}
return nil, nil, err
}

funcName := "loadConfig"
Expand Down
174 changes: 174 additions & 0 deletions config_test.go
@@ -0,0 +1,174 @@
// Copyright 2016 Daniel Krawisz.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package main

import (
"testing"
"io/ioutil"
"os"
"strconv"
"fmt"
)

// If config files exist while we are doing
var oldDefaultConfigFile []byte = nil
var oldConfigFile []byte = nil
var oldConfigFilename *string = nil

func setup(defaultConfigContents, configFileContents, configFilename *string) error {
var err error

// Check if a default config file exists. If so, save it and remove it.
if _, err = os.Stat(defaultConfigFile); !os.IsNotExist(err) {
oldDefaultConfigFile, err = ioutil.ReadFile(defaultConfigFile)

if err != nil {
return err
}

err = os.Remove(defaultConfigFile)
if err != nil {
oldDefaultConfigFile = nil
return err
}
}

// Check if defaultConfigContents is set. If so, make a config file.
if defaultConfigContents != nil {
err = ioutil.WriteFile(defaultConfigFile, []byte(*defaultConfigContents), 0644)
if err != nil {
cleanup()
return nil
}
}

// Check if configFilePath is set and is not equal to the default
// path.
if configFilename == nil || *configFilename == defaultConfigFile {
return nil
} else {
oldConfigFilename = configFilename
}

// If the file exists, save it.
if _, err = os.Stat(*configFilename); !os.IsNotExist(err) {
oldConfigFile, err = ioutil.ReadFile(*configFilename)

if err != nil {
return err
}

err = os.Remove(*configFilename)
if err != nil {
oldConfigFile = nil
return err
}
}

if configFileContents != nil {
err = ioutil.WriteFile(*configFilename, []byte(*configFileContents), 0644)
if err != nil {
cleanup()
return nil
}
}

return nil
}

func cleanup() {
if oldConfigFile == nil {
if _, err := os.Stat(defaultConfigFile); !os.IsNotExist(err) {
os.Remove(defaultConfigFile)
}
} else {
ioutil.WriteFile(defaultConfigFile, oldDefaultConfigFile, 0644)
}

if oldConfigFilename != nil {
if oldConfigFile == nil {
os.Remove(*oldConfigFilename)
} else {
ioutil.WriteFile(*oldConfigFilename, oldDefaultConfigFile, 0644)
}
}

oldConfigFile = nil;
oldConfigFilename = nil;
oldDefaultConfigFile = nil;
}

func testConfig(t *testing.T, testId int, expected uint64, cmdLine *uint64, defaultConfig *uint64, config *uint64, configFile *string) {
var defaultConfigContents *string
var configFileContents *string
var commandLine []string = make([]string, 0)

defer cleanup()

// first construct the command-line arguments.
if cmdLine != nil {
commandLine = append(commandLine, fmt.Sprintf("--maxpeers=%s", strconv.FormatUint(*cmdLine, 10)))
}
if configFile != nil {
commandLine = append(commandLine, fmt.Sprintf("--configfile=%s", *configFile))
}

// Make the default config file.
if defaultConfig != nil {
var dcc string = fmt.Sprintf("maxpeers=%s", strconv.FormatUint(*defaultConfig, 10))
defaultConfigContents = &dcc
}

// Make the extra config file.
if config != nil {
var cc string = fmt.Sprintf("maxpeers=%s", strconv.FormatUint(*config, 10))
configFileContents = &cc
}

// Set up the test.
err := setup(defaultConfigContents, configFileContents, configFile)
if err != nil {
t.Fail()
}

cfg, _, err := LoadConfig("test", commandLine)

if cfg == nil {
t.Errorf("Error, test id %d: nil config returned! %s", testId, err.Error())
return
}

if cfg.MaxPeers != int(expected) {
t.Errorf("Error, test id %d: expected %d got %d.", testId, expected, cfg.MaxPeers)
}

}

func TestLoadConfig(t *testing.T) {

// Test that an option is correctly set by default when
// no such option is specified in the default config file
// or on the command line.
testConfig(t, 1, defaultMaxPeers, nil, nil, nil, nil)

// Test that an option is correctly set when specified
// on the command line.
var q uint64 = 97
testConfig(t, 2, q, &q, nil, nil, nil)

// Test that an option is correctly set when specified
// in the default config file without a command line
// option set.
var cfg string = "altbmd.conf"
testConfig(t, 3, q, nil, &q, nil, nil)
testConfig(t, 4, q, nil, nil, &q, &cfg)

// Test that an option is correctly set when specified
// on the command line and that it overwrites the
// option in the config file.
var z uint64 = 39
testConfig(t, 5, q, &q, &z, nil, nil)
testConfig(t, 6, q, &q, nil, &z, &cfg)
}
2 changes: 1 addition & 1 deletion peer_test.go
Expand Up @@ -146,7 +146,7 @@ func init() {

// Load config
var err error
cfg, _, err = loadConfig(true)
cfg, _, err = loadConfig()
if err != nil {
panic(fmt.Sprint("Config failed to load: ", err))
}
Expand Down

0 comments on commit 13934c2

Please sign in to comment.