Merge pull request #578 from nats-io/add_configure_options

[FIXED] Override from command line not always working
This commit is contained in:
Ivan Kozlovic
2017-09-07 09:46:41 -06:00
committed by GitHub
4 changed files with 527 additions and 266 deletions

222
main.go
View File

@@ -5,10 +5,7 @@ package main
import (
"flag"
"fmt"
"net"
"net/url"
"os"
"strings"
"github.com/nats-io/gnatsd/server"
)
@@ -66,126 +63,14 @@ func usage() {
}
func main() {
// Server Options
opts := &server.Options{}
// Create a FlagSet and sets the usage
fs := flag.NewFlagSet("nats-server", flag.ExitOnError)
fs.Usage = usage
var (
showVersion bool
debugAndTrace bool
configFile string
signal string
showTLSHelp bool
)
// Parse flags
flag.IntVar(&opts.Port, "port", 0, "Port to listen on.")
flag.IntVar(&opts.Port, "p", 0, "Port to listen on.")
flag.StringVar(&opts.Host, "addr", "", "Network host to listen on.")
flag.StringVar(&opts.Host, "a", "", "Network host to listen on.")
flag.StringVar(&opts.Host, "net", "", "Network host to listen on.")
flag.BoolVar(&opts.Debug, "D", false, "Enable Debug logging.")
flag.BoolVar(&opts.Debug, "debug", false, "Enable Debug logging.")
flag.BoolVar(&opts.Trace, "V", false, "Enable Trace logging.")
flag.BoolVar(&opts.Trace, "trace", false, "Enable Trace logging.")
flag.BoolVar(&debugAndTrace, "DV", false, "Enable Debug and Trace logging.")
flag.BoolVar(&opts.Logtime, "T", true, "Timestamp log entries.")
flag.BoolVar(&opts.Logtime, "logtime", true, "Timestamp log entries.")
flag.StringVar(&opts.Username, "user", "", "Username required for connection.")
flag.StringVar(&opts.Password, "pass", "", "Password required for connection.")
flag.StringVar(&opts.Authorization, "auth", "", "Authorization token required for connection.")
flag.IntVar(&opts.HTTPPort, "m", 0, "HTTP Port for /varz, /connz endpoints.")
flag.IntVar(&opts.HTTPPort, "http_port", 0, "HTTP Port for /varz, /connz endpoints.")
flag.IntVar(&opts.HTTPSPort, "ms", 0, "HTTPS Port for /varz, /connz endpoints.")
flag.IntVar(&opts.HTTPSPort, "https_port", 0, "HTTPS Port for /varz, /connz endpoints.")
flag.StringVar(&configFile, "c", "", "Configuration file.")
flag.StringVar(&configFile, "config", "", "Configuration file.")
flag.StringVar(&signal, "sl", "", "Send signal to gnatsd process (stop, quit, reopen, reload)")
flag.StringVar(&signal, "signal", "", "Send signal to gnatsd process (stop, quit, reopen, reload)")
flag.StringVar(&opts.PidFile, "P", "", "File to store process pid.")
flag.StringVar(&opts.PidFile, "pid", "", "File to store process pid.")
flag.StringVar(&opts.LogFile, "l", "", "File to store logging output.")
flag.StringVar(&opts.LogFile, "log", "", "File to store logging output.")
flag.BoolVar(&opts.Syslog, "s", false, "Enable syslog as log method.")
flag.BoolVar(&opts.Syslog, "syslog", false, "Enable syslog as log method..")
flag.StringVar(&opts.RemoteSyslog, "r", "", "Syslog server addr (udp://localhost:514).")
flag.StringVar(&opts.RemoteSyslog, "remote_syslog", "", "Syslog server addr (udp://localhost:514).")
flag.BoolVar(&showVersion, "version", false, "Print version information.")
flag.BoolVar(&showVersion, "v", false, "Print version information.")
flag.IntVar(&opts.ProfPort, "profile", 0, "Profiling HTTP port")
flag.StringVar(&opts.RoutesStr, "routes", "", "Routes to actively solicit a connection.")
flag.StringVar(&opts.Cluster.ListenStr, "cluster", "", "Cluster url from which members can solicit routes.")
flag.StringVar(&opts.Cluster.ListenStr, "cluster_listen", "", "Cluster url from which members can solicit routes.")
flag.BoolVar(&opts.Cluster.NoAdvertise, "no_advertise", false, "Advertise known cluster IPs to clients.")
flag.IntVar(&opts.Cluster.ConnectRetries, "connect_retries", 0, "For implicit routes, number of connect retries")
flag.BoolVar(&showTLSHelp, "help_tls", false, "TLS help.")
flag.BoolVar(&opts.TLS, "tls", false, "Enable TLS.")
flag.BoolVar(&opts.TLSVerify, "tlsverify", false, "Enable TLS with client verification.")
flag.StringVar(&opts.TLSCert, "tlscert", "", "Server certificate file.")
flag.StringVar(&opts.TLSKey, "tlskey", "", "Private key for server certificate.")
flag.StringVar(&opts.TLSCaCert, "tlscacert", "", "Client certificate CA for verification.")
flag.Usage = func() {
fmt.Printf("%s\n", usageStr)
}
flag.Parse()
// Show version and exit
if showVersion {
server.PrintServerAndExit()
}
if showTLSHelp {
server.PrintTLSHelpAndDie()
}
// One flag can set multiple options.
if debugAndTrace {
opts.Trace, opts.Debug = true, true
}
// Process args looking for non-flag options,
// 'version' and 'help' only for now
showVersion, showHelp, err := server.ProcessCommandLineArgs(flag.CommandLine)
// Configure the options from the flags/config file
opts, err := server.ConfigureOptions(fs, os.Args[1:])
if err != nil {
server.PrintAndDie(err.Error() + usageStr)
} else if showVersion {
server.PrintServerAndExit()
} else if showHelp {
usage()
}
// Snapshot flag options.
server.FlagSnapshot = opts.Clone()
// Process signal control.
if signal != "" {
processSignal(signal)
}
// Parse config if given
if configFile != "" {
fileOpts, err := server.ProcessConfigFile(configFile)
if err != nil {
server.PrintAndDie(err.Error())
}
opts = server.MergeOptions(fileOpts, opts)
}
// Remove any host/ip that points to itself in Route
newroutes, err := server.RemoveSelfReference(opts.Cluster.Port, opts.Routes)
if err != nil {
server.PrintAndDie(err.Error())
}
opts.Routes = newroutes
// Configure TLS based on any present flags
configureTLS(opts)
// Configure cluster opts if explicitly set via flags.
err = configureClusterOpts(opts)
if err != nil {
server.PrintAndDie(err.Error())
server.PrintAndDie(err.Error() + "\n" + usageStr)
}
// Create the server with appropriate options.
@@ -199,98 +84,3 @@ func main() {
server.PrintAndDie(err.Error())
}
}
func configureTLS(opts *server.Options) {
// If no trigger flags, ignore the others
if !opts.TLS && !opts.TLSVerify {
return
}
if opts.TLSCert == "" {
server.PrintAndDie("TLS Server certificate must be present and valid.")
}
if opts.TLSKey == "" {
server.PrintAndDie("TLS Server private key must be present and valid.")
}
tc := server.TLSConfigOpts{}
tc.CertFile = opts.TLSCert
tc.KeyFile = opts.TLSKey
tc.CaFile = opts.TLSCaCert
if opts.TLSVerify {
tc.Verify = true
}
var err error
if opts.TLSConfig, err = server.GenTLSConfig(&tc); err != nil {
server.PrintAndDie(err.Error())
}
}
func configureClusterOpts(opts *server.Options) error {
// If we don't have cluster defined in the configuration
// file and no cluster listen string override, but we do
// have a routes override, we need to report misconfiguration.
if opts.Cluster.ListenStr == "" && opts.Cluster.Host == "" &&
opts.Cluster.Port == 0 {
if opts.RoutesStr != "" {
server.PrintAndDie("Solicited routes require cluster capabilities, e.g. --cluster.")
}
return nil
}
// If cluster flag override, process it
if opts.Cluster.ListenStr != "" {
clusterURL, err := url.Parse(opts.Cluster.ListenStr)
if err != nil {
return err
}
h, p, err := net.SplitHostPort(clusterURL.Host)
if err != nil {
return err
}
opts.Cluster.Host = h
_, err = fmt.Sscan(p, &opts.Cluster.Port)
if err != nil {
return err
}
if clusterURL.User != nil {
pass, hasPassword := clusterURL.User.Password()
if !hasPassword {
return fmt.Errorf("Expected cluster password to be set.")
}
opts.Cluster.Password = pass
user := clusterURL.User.Username()
opts.Cluster.Username = user
} else {
// Since we override from flag and there is no user/pwd, make
// sure we clear what we may have gotten from config file.
opts.Cluster.Username = ""
opts.Cluster.Password = ""
}
}
// If we have routes but no config file, fill in here.
if opts.RoutesStr != "" && opts.Routes == nil {
opts.Routes = server.RoutesFromStr(opts.RoutesStr)
}
return nil
}
func processSignal(signal string) {
var (
pid string
commandAndPid = strings.Split(signal, "=")
)
if l := len(commandAndPid); l == 2 {
pid = commandAndPid[1]
} else if l > 2 {
usage()
}
if err := server.ProcessSignal(server.Command(commandAndPid[0]), pid); err != nil {
server.PrintAndDie(err.Error())
}
os.Exit(0)
}

View File

@@ -5,6 +5,8 @@ package server
import (
"crypto/tls"
"crypto/x509"
"errors"
"flag"
"fmt"
"io/ioutil"
"net"
@@ -18,7 +20,7 @@ import (
"github.com/nats-io/gnatsd/util"
)
// Options for clusters.
// ClusterOpts are options for clusters.
type ClusterOpts struct {
Host string `json:"addr"`
Port int `json:"cluster_port"`
@@ -156,15 +158,36 @@ Available cipher suites include:
// ProcessConfigFile processes a configuration file.
// FIXME(dlc): Hacky
func ProcessConfigFile(configFile string) (*Options, error) {
opts := &Options{ConfigFile: configFile}
opts := &Options{}
if err := opts.ProcessConfigFile(configFile); err != nil {
return nil, err
}
return opts, nil
}
// ProcessConfigFile updates the Options structure with options
// present in the given configuration file.
// This version is convenient if one wants to set some default
// options and then override them with what is in the config file.
// For instance, this version allows you to do something such as:
//
// opts := &Options{Debug: true}
// opts.ProcessConfigFile(myConfigFile)
//
// If the config file contains "debug: false", after this call,
// opts.Debug would really be false. It would be impossible to
// achieve that with the non receiver ProcessConfigFile() version,
// since one would not know after the call if "debug" was not present
// or was present but set to false.
func (o *Options) ProcessConfigFile(configFile string) error {
o.ConfigFile = configFile
if configFile == "" {
return opts, nil
return nil
}
m, err := conf.ParseFile(configFile)
if err != nil {
return nil, err
return err
}
for k, v := range m {
@@ -172,113 +195,113 @@ func ProcessConfigFile(configFile string) (*Options, error) {
case "listen":
hp, err := parseListen(v)
if err != nil {
return nil, err
return err
}
opts.Host = hp.host
opts.Port = hp.port
o.Host = hp.host
o.Port = hp.port
case "port":
opts.Port = int(v.(int64))
o.Port = int(v.(int64))
case "host", "net":
opts.Host = v.(string)
o.Host = v.(string)
case "debug":
opts.Debug = v.(bool)
o.Debug = v.(bool)
case "trace":
opts.Trace = v.(bool)
o.Trace = v.(bool)
case "logtime":
opts.Logtime = v.(bool)
o.Logtime = v.(bool)
case "authorization":
am := v.(map[string]interface{})
auth, err := parseAuthorization(am)
if err != nil {
return nil, err
return err
}
opts.Username = auth.user
opts.Password = auth.pass
opts.Authorization = auth.token
o.Username = auth.user
o.Password = auth.pass
o.Authorization = auth.token
if (auth.user != "" || auth.pass != "") && auth.token != "" {
return nil, fmt.Errorf("Cannot have a user/pass and token")
return fmt.Errorf("Cannot have a user/pass and token")
}
opts.AuthTimeout = auth.timeout
o.AuthTimeout = auth.timeout
// Check for multiple users defined
if auth.users != nil {
if auth.user != "" {
return nil, fmt.Errorf("Can not have a single user/pass and a users array")
return fmt.Errorf("Can not have a single user/pass and a users array")
}
if auth.token != "" {
return nil, fmt.Errorf("Can not have a token and a users array")
return fmt.Errorf("Can not have a token and a users array")
}
opts.Users = auth.users
o.Users = auth.users
}
case "http":
hp, err := parseListen(v)
if err != nil {
return nil, err
return err
}
opts.HTTPHost = hp.host
opts.HTTPPort = hp.port
o.HTTPHost = hp.host
o.HTTPPort = hp.port
case "https":
hp, err := parseListen(v)
if err != nil {
return nil, err
return err
}
opts.HTTPHost = hp.host
opts.HTTPSPort = hp.port
o.HTTPHost = hp.host
o.HTTPSPort = hp.port
case "http_port", "monitor_port":
opts.HTTPPort = int(v.(int64))
o.HTTPPort = int(v.(int64))
case "https_port":
opts.HTTPSPort = int(v.(int64))
o.HTTPSPort = int(v.(int64))
case "cluster":
cm := v.(map[string]interface{})
if err := parseCluster(cm, opts); err != nil {
return nil, err
if err := parseCluster(cm, o); err != nil {
return err
}
case "logfile", "log_file":
opts.LogFile = v.(string)
o.LogFile = v.(string)
case "syslog":
opts.Syslog = v.(bool)
o.Syslog = v.(bool)
case "remote_syslog":
opts.RemoteSyslog = v.(string)
o.RemoteSyslog = v.(string)
case "pidfile", "pid_file":
opts.PidFile = v.(string)
o.PidFile = v.(string)
case "prof_port":
opts.ProfPort = int(v.(int64))
o.ProfPort = int(v.(int64))
case "max_control_line":
opts.MaxControlLine = int(v.(int64))
o.MaxControlLine = int(v.(int64))
case "max_payload":
opts.MaxPayload = int(v.(int64))
o.MaxPayload = int(v.(int64))
case "max_connections", "max_conn":
opts.MaxConn = int(v.(int64))
o.MaxConn = int(v.(int64))
case "ping_interval":
opts.PingInterval = time.Duration(int(v.(int64))) * time.Second
o.PingInterval = time.Duration(int(v.(int64))) * time.Second
case "ping_max":
opts.MaxPingsOut = int(v.(int64))
o.MaxPingsOut = int(v.(int64))
case "tls":
tlsm := v.(map[string]interface{})
tc, err := parseTLS(tlsm)
if err != nil {
return nil, err
return err
}
if opts.TLSConfig, err = GenTLSConfig(tc); err != nil {
return nil, err
if o.TLSConfig, err = GenTLSConfig(tc); err != nil {
return err
}
opts.TLSTimeout = tc.Timeout
o.TLSTimeout = tc.Timeout
case "write_deadline":
wd, ok := v.(string)
if ok {
dur, err := time.ParseDuration(wd)
if err != nil {
return nil, fmt.Errorf("error parsing write_deadline: %v", err)
return fmt.Errorf("error parsing write_deadline: %v", err)
}
opts.WriteDeadline = dur
o.WriteDeadline = dur
} else {
// Backward compatible with old type, assume this is the
// number of seconds.
opts.WriteDeadline = time.Duration(v.(int64)) * time.Second
o.WriteDeadline = time.Duration(v.(int64)) * time.Second
fmt.Printf("WARNING: write_deadline should be converted to a duration\n")
}
}
}
return opts, nil
return nil
}
// hostPort is simple struct to hold parsed listen/addr strings.
@@ -893,3 +916,260 @@ func processOptions(opts *Options) {
opts.WriteDeadline = DEFAULT_FLUSH_DEADLINE
}
}
// ConfigureOptions accepts a flag set and augment it with NATS Server
// specific flags. On success, an options structure is returned configured
// based on the selected flags and/or configuration file.
// The command line options take precedence to the ones in the configuration file.
func ConfigureOptions(fs *flag.FlagSet, args []string) (*Options, error) {
opts := &Options{}
var (
showVersion bool
showHelp bool
showTLSHelp bool
signal string
configFile string
err error
)
fs.IntVar(&opts.Port, "port", 0, "Port to listen on.")
fs.IntVar(&opts.Port, "p", 0, "Port to listen on.")
fs.StringVar(&opts.Host, "addr", "", "Network host to listen on.")
fs.StringVar(&opts.Host, "a", "", "Network host to listen on.")
fs.StringVar(&opts.Host, "net", "", "Network host to listen on.")
fs.BoolVar(&opts.Debug, "D", false, "Enable Debug logging.")
fs.BoolVar(&opts.Debug, "debug", false, "Enable Debug logging.")
fs.BoolVar(&opts.Trace, "V", false, "Enable Trace logging.")
fs.BoolVar(&opts.Trace, "trace", false, "Enable Trace logging.")
fs.Bool("DV", false, "Enable Debug and Trace logging.")
fs.BoolVar(&opts.Logtime, "T", true, "Timestamp log entries.")
fs.BoolVar(&opts.Logtime, "logtime", true, "Timestamp log entries.")
fs.StringVar(&opts.Username, "user", "", "Username required for connection.")
fs.StringVar(&opts.Password, "pass", "", "Password required for connection.")
fs.StringVar(&opts.Authorization, "auth", "", "Authorization token required for connection.")
fs.IntVar(&opts.HTTPPort, "m", 0, "HTTP Port for /varz, /connz endpoints.")
fs.IntVar(&opts.HTTPPort, "http_port", 0, "HTTP Port for /varz, /connz endpoints.")
fs.IntVar(&opts.HTTPSPort, "ms", 0, "HTTPS Port for /varz, /connz endpoints.")
fs.IntVar(&opts.HTTPSPort, "https_port", 0, "HTTPS Port for /varz, /connz endpoints.")
fs.StringVar(&configFile, "c", "", "Configuration file.")
fs.StringVar(&configFile, "config", "", "Configuration file.")
fs.StringVar(&signal, "sl", "", "Send signal to gnatsd process (stop, quit, reopen, reload)")
fs.StringVar(&signal, "signal", "", "Send signal to gnatsd process (stop, quit, reopen, reload)")
fs.StringVar(&opts.PidFile, "P", "", "File to store process pid.")
fs.StringVar(&opts.PidFile, "pid", "", "File to store process pid.")
fs.StringVar(&opts.LogFile, "l", "", "File to store logging output.")
fs.StringVar(&opts.LogFile, "log", "", "File to store logging output.")
fs.BoolVar(&opts.Syslog, "s", false, "Enable syslog as log method.")
fs.BoolVar(&opts.Syslog, "syslog", false, "Enable syslog as log method..")
fs.StringVar(&opts.RemoteSyslog, "r", "", "Syslog server addr (udp://localhost:514).")
fs.StringVar(&opts.RemoteSyslog, "remote_syslog", "", "Syslog server addr (udp://localhost:514).")
fs.BoolVar(&showVersion, "version", false, "Print version information.")
fs.BoolVar(&showVersion, "v", false, "Print version information.")
fs.IntVar(&opts.ProfPort, "profile", 0, "Profiling HTTP port")
fs.StringVar(&opts.RoutesStr, "routes", "", "Routes to actively solicit a connection.")
fs.StringVar(&opts.Cluster.ListenStr, "cluster", "", "Cluster url from which members can solicit routes.")
fs.StringVar(&opts.Cluster.ListenStr, "cluster_listen", "", "Cluster url from which members can solicit routes.")
fs.BoolVar(&opts.Cluster.NoAdvertise, "no_advertise", false, "Advertise known cluster IPs to clients.")
fs.IntVar(&opts.Cluster.ConnectRetries, "connect_retries", 0, "For implicit routes, number of connect retries")
fs.BoolVar(&showTLSHelp, "help_tls", false, "TLS help.")
fs.BoolVar(&opts.TLS, "tls", false, "Enable TLS.")
fs.BoolVar(&opts.TLSVerify, "tlsverify", false, "Enable TLS with client verification.")
fs.StringVar(&opts.TLSCert, "tlscert", "", "Server certificate file.")
fs.StringVar(&opts.TLSKey, "tlskey", "", "Private key for server certificate.")
fs.StringVar(&opts.TLSCaCert, "tlscacert", "", "Client certificate CA for verification.")
// The flags definition above set "default" values to some of the options.
// Calling Parse() here will override the default options with any value
// specified from the command line. This is ok. We will then update the
// options with the content of the configuration file (if present), and then,
// call Parse() again to override the default+config with command line values.
// Calling Parse() before processing config file is necessary since configFile
// itself is a command line argument, and also Parse() is required in order
// to know if user wants simply to show "help" or "version", etc...
fs.Parse(args)
// Show version and exit
if showVersion {
PrintServerAndExit()
}
if showTLSHelp {
PrintTLSHelpAndDie()
}
// Process args looking for non-flag options,
// 'version' and 'help' only for now
showVersion, showHelp, err = ProcessCommandLineArgs(fs)
if err != nil {
return nil, err
} else if showVersion {
PrintServerAndExit()
} else if showHelp {
fs.Usage()
return nil, nil
}
// Snapshot flag options.
FlagSnapshot = opts.Clone()
// Process signal control.
if signal != "" {
if err := processSignal(signal); err != nil {
return nil, err
}
}
// Parse config if given
if configFile != "" {
// This will update the options with values from the config file.
if err := opts.ProcessConfigFile(configFile); err != nil {
return nil, err
}
// Call this again to override config file options with options from command line.
fs.Parse(args)
}
// Special handling of some flags
var (
flagErr error
tlsDisabled bool
tlsOverride bool
)
fs.Visit(func(f *flag.Flag) {
// short-circuit if an error was encountered
if flagErr != nil {
return
}
if strings.HasPrefix(f.Name, "tls") {
if f.Name == "tls" {
if !opts.TLS {
// User has specified "-tls=false", we need to disable TLS
opts.TLSConfig = nil
tlsDisabled = true
tlsOverride = false
return
}
tlsOverride = true
} else if !tlsDisabled {
tlsOverride = true
}
} else {
switch f.Name {
case "DV":
// Check value to support -DV=false
boolValue, _ := strconv.ParseBool(f.Value.String())
opts.Trace, opts.Debug = boolValue, boolValue
case "cluster", "cluster_listen":
// Override cluster config if explicitly set via flags.
flagErr = overrideCluster(opts)
case "routes":
// Keep in mind that the flag has updated opts.RoutesStr at this point.
if opts.RoutesStr == "" {
// Set routes array to nil since routes string is empty
opts.Routes = nil
return
}
routeUrls := RoutesFromStr(opts.RoutesStr)
opts.Routes = routeUrls
}
}
})
if flagErr != nil {
return nil, flagErr
}
// This will be true if some of the `-tls` params have been set and
// `-tls=false` has not been set.
if tlsOverride {
if err := overrideTLS(opts); err != nil {
return nil, err
}
}
// If we don't have cluster defined in the configuration
// file and no cluster listen string override, but we do
// have a routes override, we need to report misconfiguration.
if opts.RoutesStr != "" && opts.Cluster.ListenStr == "" && opts.Cluster.Host == "" && opts.Cluster.Port == 0 {
return nil, errors.New("solicited routes require cluster capabilities, e.g. --cluster")
}
return opts, nil
}
// overrideTLS is called when at least "-tls=true" has been set.
func overrideTLS(opts *Options) error {
if opts.TLSCert == "" {
return errors.New("TLS Server certificate must be present and valid")
}
if opts.TLSKey == "" {
return errors.New("TLS Server private key must be present and valid")
}
tc := TLSConfigOpts{}
tc.CertFile = opts.TLSCert
tc.KeyFile = opts.TLSKey
tc.CaFile = opts.TLSCaCert
tc.Verify = opts.TLSVerify
var err error
opts.TLSConfig, err = GenTLSConfig(&tc)
return err
}
// overrideCluster updates Options.Cluster if that flag "cluster" (or "cluster_listen")
// has explicitly be set in the command line. If it is set to empty string, it will
// clear the Cluster options.
func overrideCluster(opts *Options) error {
if opts.Cluster.ListenStr == "" {
// This one is enough to disable clustering.
opts.Cluster.Port = 0
return nil
}
clusterURL, err := url.Parse(opts.Cluster.ListenStr)
if err != nil {
return err
}
h, p, err := net.SplitHostPort(clusterURL.Host)
if err != nil {
return err
}
opts.Cluster.Host = h
_, err = fmt.Sscan(p, &opts.Cluster.Port)
if err != nil {
return err
}
if clusterURL.User != nil {
pass, hasPassword := clusterURL.User.Password()
if !hasPassword {
return errors.New("expected cluster password to be set")
}
opts.Cluster.Password = pass
user := clusterURL.User.Username()
opts.Cluster.Username = user
} else {
// Since we override from flag and there is no user/pwd, make
// sure we clear what we may have gotten from config file.
opts.Cluster.Username = ""
opts.Cluster.Password = ""
}
return nil
}
func processSignal(signal string) error {
var (
pid string
commandAndPid = strings.Split(signal, "=")
)
if l := len(commandAndPid); l == 2 {
pid = commandAndPid[1]
} else if l > 2 {
return fmt.Errorf("invalid signal parameters: %v", commandAndPid[2:])
}
if err := ProcessSignal(Command(commandAndPid[0]), pid); err != nil {
return err
}
os.Exit(0)
return nil
}

View File

@@ -4,6 +4,7 @@ package server
import (
"crypto/tls"
"flag"
"io/ioutil"
"net/url"
"os"
@@ -789,3 +790,193 @@ func TestMalformedClusterAddress(t *testing.T) {
t.Fatalf("Expected an error reading config file: got %+v\n", opts)
}
}
func TestOptionsProcessConfigFile(t *testing.T) {
// Create options with default values of Debug and Trace
// that are the opposite of what is in the config file.
// Set another option that is not present in the config file.
logFileName := "test.log"
opts := &Options{
Debug: true,
Trace: false,
LogFile: logFileName,
}
configFileName := "./configs/test.conf"
if err := opts.ProcessConfigFile(configFileName); err != nil {
t.Fatalf("Error processing config file: %v", err)
}
// Verify that values are as expected
if opts.ConfigFile != configFileName {
t.Fatalf("Expected ConfigFile to be set to %q, got %v", configFileName, opts.ConfigFile)
}
if opts.Debug {
t.Fatal("Debug option should have been set to false from config file")
}
if !opts.Trace {
t.Fatal("Trace option should have been set to true from config file")
}
if opts.LogFile != logFileName {
t.Fatalf("Expected LogFile to be %q, got %q", logFileName, opts.LogFile)
}
}
func TestConfigureOptions(t *testing.T) {
// Options.Configure() will snapshot the flags. This is used by the reload code.
// We need to set it back to nil otherwise it will impact reload tests.
defer func() { FlagSnapshot = nil }()
// Ensure that Usage is called when param "help" is set
fs := flag.NewFlagSet("test", flag.ContinueOnError)
ch := make(chan bool, 1)
fs.Usage = func() {
ch <- true
}
if _, err := ConfigureOptions(fs, []string{"help"}); err != nil {
t.Fatalf("Error on configure: %v", err)
}
select {
case <-ch:
case <-time.After(time.Second):
t.Fatal("Should have invoked flag set's Usage")
}
// Helper function that expect parsing with given args to not produce an error.
mustNotFail := func(args []string) *Options {
fs := flag.NewFlagSet("test", flag.ContinueOnError)
opts, err := ConfigureOptions(fs, args)
if err != nil {
stackFatalf(t, "Error on configure: %v", err)
}
return opts
}
// Helper function that expect configuration to fail.
expectToFail := func(args []string, errContent ...string) {
fs := flag.NewFlagSet("test", flag.ContinueOnError)
opts, err := ConfigureOptions(fs, args)
if opts != nil || err == nil {
stackFatalf(t, "Expected no option and an error, got opts=%v and err=%v", opts, err)
}
for _, testErr := range errContent {
if strings.Contains(err.Error(), testErr) {
// We got the error we wanted.
return
}
}
stackFatalf(t, "Expected errors containing any of those %v, got %v", errContent, err)
}
// Basic test with port number
opts := mustNotFail([]string{"-p", "1234"})
if opts.Port != 1234 {
t.Fatalf("Expected port to be 1234, got %v", opts.Port)
}
// Should fail because of unknown parameter
expectToFail([]string{"foo"}, "command")
// Should fail because of config file missing
expectToFail([]string{"-c", "xxx.cfg"}, "file")
// Should fail because of too many args for signal command
expectToFail([]string{"-sl", "quit=pid=foo"}, "signal")
// Should fail because of invalid pid
// On windows, if not running with admin privileges, you would get access denied.
expectToFail([]string{"-sl", "quit=pid"}, "pid", "denied")
// The config file set Trace to true.
opts = mustNotFail([]string{"-c", "./configs/test.conf"})
if !opts.Trace {
t.Fatal("Trace should have been set to true")
}
// The config file set Trace to true, but was overridden by param -V=false
opts = mustNotFail([]string{"-c", "./configs/test.conf", "-V=false"})
if opts.Trace {
t.Fatal("Trace should have been set to false")
}
// The config file set Trace to true, but was overridden by param -DV=false
opts = mustNotFail([]string{"-c", "./configs/test.conf", "-DV=false"})
if opts.Debug || opts.Trace {
t.Fatal("Debug and Trace should have been set to false")
}
// The config file set Trace to true, but was overridden by param -DV
opts = mustNotFail([]string{"-c", "./configs/test.conf", "-DV"})
if !opts.Debug || !opts.Trace {
t.Fatal("Debug and Trace should have been set to true")
}
// This should fail since -cluster is missing
expectedURL, _ := url.Parse("nats://localhost:6223")
expectToFail([]string{"-routes", expectedURL.String()}, "solicited routes")
// Ensure that we can set cluster and routes from command line
opts = mustNotFail([]string{"-cluster", "nats://localhost:6222", "-routes", expectedURL.String()})
if opts.Cluster.ListenStr != "nats://localhost:6222" {
t.Fatalf("Unexpected Cluster.ListenStr=%q", opts.Cluster.ListenStr)
}
if opts.RoutesStr != "nats://localhost:6223" || len(opts.Routes) != 1 || opts.Routes[0].String() != expectedURL.String() {
t.Fatalf("Unexpected RoutesStr: %q and Routes: %v", opts.RoutesStr, opts.Routes)
}
// Use a config with cluster configuration and explicit route defined.
// Override with empty routes string.
opts = mustNotFail([]string{"-c", "./configs/srv_a.conf", "-routes", ""})
if opts.RoutesStr != "" || len(opts.Routes) != 0 {
t.Fatalf("Unexpected RoutesStr: %q and Routes: %v", opts.RoutesStr, opts.Routes)
}
// Use a config with cluster configuration and override cluster listen string
expectedURL, _ = url.Parse("nats-route://ruser:top_secret@127.0.0.1:7246")
opts = mustNotFail([]string{"-c", "./configs/srv_a.conf", "-cluster", "nats://ivan:pwd@localhost:6222"})
if opts.Cluster.Username != "ivan" || opts.Cluster.Password != "pwd" || opts.Cluster.Port != 6222 ||
len(opts.Routes) != 1 || opts.Routes[0].String() != expectedURL.String() {
t.Fatalf("Unexpected Cluster and/or Routes: %#v - %v", opts.Cluster, opts.Routes)
}
// Disable clustering from command line
opts = mustNotFail([]string{"-c", "./configs/srv_a.conf", "-cluster", ""})
if opts.Cluster.Port != 0 {
t.Fatalf("Unexpected Cluster: %v", opts.Cluster)
}
// Various erros due to malformed cluster listen string.
// (adding -routes to have more than 1 set flag to check
// that Visit() stops when an error is found).
expectToFail([]string{"-cluster", ":", "-routes", ""}, "protocol")
expectToFail([]string{"-cluster", "nats://localhost", "-routes", ""}, "port")
expectToFail([]string{"-cluster", "nats://localhost:xxx", "-routes", ""}, "integer")
expectToFail([]string{"-cluster", "nats://ivan:localhost:6222", "-routes", ""}, "colons")
expectToFail([]string{"-cluster", "nats://ivan@localhost:6222", "-routes", ""}, "password")
// Override config file's TLS configuration from command line, and completely disable TLS
opts = mustNotFail([]string{"-c", "./configs/tls.conf", "-tls=false"})
if opts.TLSConfig != nil || opts.TLS {
t.Fatal("Expected TLS to be disabled")
}
// Override config file's TLS configuration from command line, and force TLS verification.
// However, since TLS config has to be regenerated, user need to provide -tlscert and -tlskey too.
// So this should fail.
expectToFail([]string{"-c", "./configs/tls.conf", "-tlsverify"}, "valid")
// Now same than above, but with all valid params.
opts = mustNotFail([]string{"-c", "./configs/tls.conf", "-tlsverify", "-tlscert", "./configs/certs/server.pem", "-tlskey", "./configs/certs/key.pem"})
if opts.TLSConfig == nil || !opts.TLSVerify {
t.Fatal("Expected TLS to be configured and force verification")
}
// Configure TLS, but some TLS params missing
expectToFail([]string{"-tls"}, "valid")
expectToFail([]string{"-tls", "-tlscert", "./configs/certs/server.pem"}, "valid")
// One of the file does not exist
expectToFail([]string{"-tls", "-tlscert", "./configs/certs/server.pem", "-tlskey", "./configs/certs/notfound.pem"}, "file")
// Configure TLS and check that this results in a TLSConfig option.
opts = mustNotFail([]string{"-tls", "-tlscert", "./configs/certs/server.pem", "-tlskey", "./configs/certs/key.pem"})
if opts.TLSConfig == nil || !opts.TLS {
t.Fatal("Expected TLSConfig to be set")
}
}

View File

@@ -212,7 +212,7 @@ func ProcessCommandLineArgs(cmd *flag.FlagSet) (showVersion bool, showHelp bool,
case "help":
return false, true, nil
default:
return false, false, fmt.Errorf("Unrecognized command: %q\n", arg)
return false, false, fmt.Errorf("unrecognized command: %q", arg)
}
}