Merge pull request #783 from wilmerpaulino/config-node-dir

config+cmd/lncli: add flags to specify the base directory for lnd and supported backend nodes
This commit is contained in:
Olaoluwa Osuntokun 2018-03-12 20:31:49 -07:00 committed by GitHub
commit 3a53752d90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 123 additions and 69 deletions

View File

@ -8,6 +8,7 @@ import (
"fmt"
"io/ioutil"
"os"
"os/user"
"path/filepath"
"strings"
@ -28,9 +29,9 @@ const (
)
var (
lndHomeDir = btcutil.AppDataDir("lnd", false)
defaultTLSCertPath = filepath.Join(lndHomeDir, defaultTLSCertFilename)
defaultMacaroonPath = filepath.Join(lndHomeDir, defaultMacaroonFilename)
defaultLndDir = btcutil.AppDataDir("lnd", false)
defaultTLSCertPath = filepath.Join(defaultLndDir, defaultTLSCertFilename)
defaultMacaroonPath = filepath.Join(defaultLndDir, defaultMacaroonFilename)
)
func fatal(err error) {
@ -59,6 +60,27 @@ func getClient(ctx *cli.Context) (lnrpc.LightningClient, func()) {
}
func getClientConn(ctx *cli.Context, skipMacaroons bool) *grpc.ClientConn {
lndDir := cleanAndExpandPath(ctx.GlobalString("lnddir"))
if lndDir != defaultLndDir {
// If a custom lnd directory was set, we'll also check if custom
// paths for the TLS cert and macaroon file were set as well. If
// not, we'll override their paths so they can be found within
// the custom lnd directory set. This allows us to set a custom
// lnd directory, along with custom paths to the TLS cert and
// macaroon file.
tlsCertPath := cleanAndExpandPath(ctx.GlobalString("tlscertpath"))
if tlsCertPath == defaultTLSCertPath {
ctx.GlobalSet("tlscertpath",
filepath.Join(lndDir, defaultTLSCertFilename))
}
macPath := cleanAndExpandPath(ctx.GlobalString("macaroonpath"))
if macPath == defaultMacaroonPath {
ctx.GlobalSet("no-macaroons",
filepath.Join(lndDir, defaultMacaroonFilename))
}
}
// Load the specified TLS certificate and build transport credentials
// with it.
tlsCertPath := cleanAndExpandPath(ctx.GlobalString("tlscertpath"))
@ -136,6 +158,11 @@ func main() {
Value: "localhost:10009",
Usage: "host:port of ln daemon",
},
cli.StringFlag{
Name: "lnddir",
Value: defaultLndDir,
Usage: "path to lnd's base directory",
},
cli.StringFlag{
Name: "tlscertpath",
Value: defaultTLSCertPath,
@ -210,7 +237,15 @@ func main() {
func cleanAndExpandPath(path string) string {
// Expand initial ~ to OS specific home directory.
if strings.HasPrefix(path, "~") {
homeDir := filepath.Dir(lndHomeDir)
var homeDir string
user, err := user.Current()
if err == nil {
homeDir = user.HomeDir
} else {
homeDir = os.Getenv("HOME")
}
path = strings.Replace(path, "~", homeDir, 1)
}

149
config.go
View File

@ -9,6 +9,7 @@ import (
"io/ioutil"
"net"
"os"
"os/user"
"path"
"path/filepath"
"regexp"
@ -66,23 +67,22 @@ const (
)
var (
// TODO(roasbeef): base off of datadir instead?
lndHomeDir = btcutil.AppDataDir("lnd", false)
defaultConfigFile = filepath.Join(lndHomeDir, defaultConfigFilename)
defaultDataDir = filepath.Join(lndHomeDir, defaultDataDirname)
defaultTLSCertPath = filepath.Join(lndHomeDir, defaultTLSCertFilename)
defaultTLSKeyPath = filepath.Join(lndHomeDir, defaultTLSKeyFilename)
defaultAdminMacPath = filepath.Join(lndHomeDir, defaultAdminMacFilename)
defaultReadMacPath = filepath.Join(lndHomeDir, defaultReadMacFilename)
defaultLogDir = filepath.Join(lndHomeDir, defaultLogDirname)
defaultLndDir = btcutil.AppDataDir("lnd", false)
defaultConfigFile = filepath.Join(defaultLndDir, defaultConfigFilename)
defaultDataDir = filepath.Join(defaultLndDir, defaultDataDirname)
defaultTLSCertPath = filepath.Join(defaultLndDir, defaultTLSCertFilename)
defaultTLSKeyPath = filepath.Join(defaultLndDir, defaultTLSKeyFilename)
defaultAdminMacPath = filepath.Join(defaultLndDir, defaultAdminMacFilename)
defaultReadMacPath = filepath.Join(defaultLndDir, defaultReadMacFilename)
defaultLogDir = filepath.Join(defaultLndDir, defaultLogDirname)
btcdHomeDir = btcutil.AppDataDir("btcd", false)
defaultBtcdRPCCertFile = filepath.Join(btcdHomeDir, "rpc.cert")
defaultBtcdDir = btcutil.AppDataDir("btcd", false)
defaultBtcdRPCCertFile = filepath.Join(defaultBtcdDir, "rpc.cert")
ltcdHomeDir = btcutil.AppDataDir("ltcd", false)
defaultLtcdRPCCertFile = filepath.Join(ltcdHomeDir, "rpc.cert")
defaultLtcdDir = btcutil.AppDataDir("ltcd", false)
defaultLtcdRPCCertFile = filepath.Join(defaultLtcdDir, "rpc.cert")
bitcoindHomeDir = btcutil.AppDataDir("bitcoin", false)
defaultBitcoindDir = btcutil.AppDataDir("bitcoin", false)
)
type chainConfig struct {
@ -112,6 +112,7 @@ type neutrinoConfig struct {
}
type btcdConfig struct {
Dir string `long:"dir" description:"The base directory that contains the node's data, logs, configuration file, etc."`
RPCHost string `long:"rpchost" description:"The daemon's rpc listening address. If a port is omitted, then the default port for the selected chain parameters will be used."`
RPCUser string `long:"rpcuser" description:"Username for RPC connections"`
RPCPass string `long:"rpcpass" default-mask:"-" description:"Password for RPC connections"`
@ -120,6 +121,7 @@ type btcdConfig struct {
}
type bitcoindConfig struct {
Dir string `long:"dir" description:"The base directory that contains the node's data, logs, configuration file, etc."`
RPCHost string `long:"rpchost" description:"The daemon's rpc listening address. If a port is omitted, then the default port for the selected chain parameters will be used."`
RPCUser string `long:"rpcuser" description:"Username for RPC connections"`
RPCPass string `long:"rpcpass" default-mask:"-" description:"Password for RPC connections"`
@ -146,10 +148,11 @@ type torConfig struct {
type config struct {
ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"`
LndDir string `long:"lnddir" description:"The base directory that contains lnd's data, logs, configuration file, etc."`
ConfigFile string `long:"C" long:"configfile" description:"Path to configuration file"`
DataDir string `short:"b" long:"datadir" description:"The directory to store lnd's data within"`
TLSCertPath string `long:"tlscertpath" description:"Path to TLS certificate for lnd's RPC and REST services"`
TLSKeyPath string `long:"tlskeypath" description:"Path to TLS private key for lnd's RPC and REST services"`
TLSCertPath string `long:"tlscertpath" description:"Path to write the TLS certificate for lnd's RPC and REST services"`
TLSKeyPath string `long:"tlskeypath" description:"Path to write the TLS private key for lnd's RPC and REST services"`
TLSExtraIP string `long:"tlsextraip" description:"Adds an extra ip to the generated certificate"`
NoMacaroons bool `long:"no-macaroons" description:"Disable macaroon authentication"`
AdminMacPath string `long:"adminmacaroonpath" description:"Path to write the admin macaroon for lnd's RPC and REST services if it doesn't exist"`
@ -208,6 +211,7 @@ type config struct {
// 4) Parse CLI options and overwrite/add any specified options
func loadConfig() (*config, error) {
defaultCfg := config{
LndDir: defaultLndDir,
ConfigFile: defaultConfigFile,
DataDir: defaultDataDir,
DebugLevel: defaultLogLevel,
@ -224,10 +228,12 @@ func loadConfig() (*config, error) {
Node: "btcd",
},
BtcdMode: &btcdConfig{
Dir: defaultBtcdDir,
RPCHost: defaultRPCHost,
RPCCert: defaultBtcdRPCCertFile,
},
BitcoindMode: &bitcoindConfig{
Dir: defaultBitcoindDir,
RPCHost: defaultRPCHost,
},
Litecoin: &chainConfig{
@ -238,6 +244,7 @@ func loadConfig() (*config, error) {
Node: "btcd",
},
LtcdMode: &btcdConfig{
Dir: defaultLtcdDir,
RPCHost: defaultRPCHost,
RPCCert: defaultLtcdRPCCertFile,
},
@ -268,9 +275,22 @@ func loadConfig() (*config, error) {
os.Exit(0)
}
// Create the home directory if it doesn't already exist.
// If the provided lnd directory is not the default, we'll modify the
// path to all of the files and directories that will live within it.
lndDir := cleanAndExpandPath(preCfg.LndDir)
if lndDir != defaultLndDir {
defaultCfg.ConfigFile = filepath.Join(lndDir, defaultConfigFilename)
defaultCfg.DataDir = filepath.Join(lndDir, defaultDataDirname)
defaultCfg.TLSCertPath = filepath.Join(lndDir, defaultTLSCertFilename)
defaultCfg.TLSKeyPath = filepath.Join(lndDir, defaultTLSKeyFilename)
defaultCfg.AdminMacPath = filepath.Join(lndDir, defaultAdminMacFilename)
defaultCfg.ReadMacPath = filepath.Join(lndDir, defaultReadMacFilename)
defaultCfg.LogDir = filepath.Join(lndDir, defaultLogDirname)
}
// Create the lnd directory if it doesn't already exist.
funcName := "loadConfig"
if err := os.MkdirAll(lndHomeDir, 0700); err != nil {
if err := os.MkdirAll(lndDir, 0700); err != nil {
// Show a nicer error message if it's because a symlink is
// linked to a directory that does not exist (probably because
// it's not mounted).
@ -281,7 +301,7 @@ func loadConfig() (*config, error) {
}
}
str := "%s: Failed to create home directory: %v"
str := "%s: Failed to create lnd directory: %v"
err := fmt.Errorf(str, funcName, err)
fmt.Fprintln(os.Stderr, err)
return nil, err
@ -290,7 +310,8 @@ func loadConfig() (*config, error) {
// Next, load any additional configuration options from the file.
var configFileError error
cfg := defaultCfg
if err := flags.IniParse(preCfg.ConfigFile, &cfg); err != nil {
configFile := cleanAndExpandPath(preCfg.ConfigFile)
if err := flags.IniParse(configFile, &cfg); err != nil {
configFileError = err
}
@ -300,6 +321,19 @@ func loadConfig() (*config, error) {
return nil, err
}
// As soon as we're done parsing configuration options, ensure all paths
// to directories and files are cleaned and expanded before attempting
// to use them later on.
cfg.DataDir = cleanAndExpandPath(cfg.DataDir)
cfg.TLSCertPath = cleanAndExpandPath(cfg.TLSCertPath)
cfg.TLSKeyPath = cleanAndExpandPath(cfg.TLSKeyPath)
cfg.AdminMacPath = cleanAndExpandPath(cfg.AdminMacPath)
cfg.ReadMacPath = cleanAndExpandPath(cfg.ReadMacPath)
cfg.LogDir = cleanAndExpandPath(cfg.LogDir)
cfg.BtcdMode.Dir = cleanAndExpandPath(cfg.BtcdMode.Dir)
cfg.LtcdMode.Dir = cleanAndExpandPath(cfg.LtcdMode.Dir)
cfg.BitcoindMode.Dir = cleanAndExpandPath(cfg.BitcoindMode.Dir)
// Setup dial and DNS resolution functions depending on the specified
// options. The default is to use the standard golang "net" package
// functions. When Tor's proxy is specified, the dial function is set to
@ -489,28 +523,12 @@ func loadConfig() (*config, error) {
cfg.ReadMacPath = filepath.Join(cfg.DataDir, defaultReadMacFilename)
}
// Append the network type to the data directory so it is "namespaced"
// per network. In addition to the block database, there are other
// pieces of data that are saved to disk such as address manager state.
// All data is specific to a network, so namespacing the data directory
// means each individual piece of serialized data does not have to
// worry about changing names per network and such.
// TODO(roasbeef): when we go full multi-chain remove the additional
// namespacing on the target chain.
cfg.DataDir = cleanAndExpandPath(cfg.DataDir)
// Append the network type to the log directory so it is "namespaced"
// per network in the same fashion as the data directory.
cfg.LogDir = cleanAndExpandPath(cfg.LogDir)
cfg.LogDir = filepath.Join(cfg.LogDir,
registeredChains.PrimaryChain().String(),
normalizeNetwork(activeNetParams.Name))
// Ensure that the paths to the TLS key and certificate files are
// expanded and cleaned.
cfg.TLSCertPath = cleanAndExpandPath(cfg.TLSCertPath)
cfg.TLSKeyPath = cleanAndExpandPath(cfg.TLSKeyPath)
// Initialize logging at the default logging level.
initLogRotator(filepath.Join(cfg.LogDir, defaultLogFilename))
@ -589,7 +607,15 @@ func loadConfig() (*config, error) {
func cleanAndExpandPath(path string) string {
// Expand initial ~ to OS specific home directory.
if strings.HasPrefix(path, "~") {
homeDir := filepath.Dir(lndHomeDir)
var homeDir string
user, err := user.Current()
if err == nil {
homeDir = user.HomeDir
} else {
homeDir = os.Getenv("HOME")
}
path = strings.Replace(path, "~", homeDir, 1)
}
@ -693,9 +719,11 @@ func noiseDial(idPriv *btcec.PrivateKey) func(net.Addr) (net.Conn, error) {
func parseRPCParams(cConfig *chainConfig, nodeConfig interface{}, net chainCode,
funcName string) error {
// If the configuration has already set the RPCUser and RPCPass, and
// if we're either not using bitcoind mode or the ZMQ path is already
// specified, we can return.
// First, we'll check our node config to make sure the RPC parameters
// were set correctly. We'll also determine the path to the conf file
// depending on the backend node.
var daemonName, confDir, confFile string
switch conf := nodeConfig.(type) {
case *btcdConfig:
// If both RPCUser and RPCPass are set, we assume those
@ -709,6 +737,17 @@ func parseRPCParams(cConfig *chainConfig, nodeConfig interface{}, net chainCode,
return fmt.Errorf("please set both or neither of " +
"btcd.rpcuser and btcd.rpcpass")
}
switch net {
case bitcoinChain:
daemonName = "btcd"
confDir = conf.Dir
confFile = "btcd"
case litecoinChain:
daemonName = "ltcd"
confDir = conf.Dir
confFile = "ltcd"
}
case *bitcoindConfig:
// If all of RPCUser, RPCPass, and ZMQPath are set, we assume
// those parameters are good to use.
@ -722,6 +761,10 @@ func parseRPCParams(cConfig *chainConfig, nodeConfig interface{}, net chainCode,
"bitcoind.rpcuser, bitcoind.rpcpass, and " +
"bitcoind.zmqpath")
}
daemonName = "bitcoind"
confDir = conf.Dir
confFile = "bitcoin"
}
// If we're in simnet mode, then the running btcd instance won't read
@ -733,33 +776,9 @@ func parseRPCParams(cConfig *chainConfig, nodeConfig interface{}, net chainCode,
return fmt.Errorf(str, funcName)
}
var daemonName, homeDir, confFile string
switch net {
case bitcoinChain:
switch cConfig.Node {
case "btcd":
daemonName = "btcd"
homeDir = btcdHomeDir
confFile = "btcd"
case "bitcoind":
daemonName = "bitcoind"
homeDir = bitcoindHomeDir
confFile = "bitcoin"
}
case litecoinChain:
switch cConfig.Node {
case "btcd":
daemonName = "ltcd"
homeDir = ltcdHomeDir
confFile = "ltcd"
case "bitcoind":
return fmt.Errorf("bitcoind mode doesn't work with Litecoin yet")
}
}
fmt.Println("Attempting automatic RPC configuration to " + daemonName)
confFile = filepath.Join(homeDir, fmt.Sprintf("%v.conf", confFile))
confFile = filepath.Join(confDir, fmt.Sprintf("%v.conf", confFile))
switch cConfig.Node {
case "btcd":
nConf := nodeConfig.(*btcdConfig)