From b95dd5dfd04ca0e8aeedc2e4dbe0802ee19b028d Mon Sep 17 00:00:00 2001 From: Tyler Treat Date: Tue, 30 May 2017 21:09:35 -0500 Subject: [PATCH] Add tests around config reload --- server/configs/reload/invalid.conf | 2 + server/configs/reload/reload.conf | 42 ++++ server/configs/reload/reload_unsupported.conf | 42 ++++ server/configs/reload/test.conf | 42 ++++ server/reload_test.go | 238 ++++++++++++++++++ 5 files changed, 366 insertions(+) create mode 100644 server/configs/reload/invalid.conf create mode 100644 server/configs/reload/reload.conf create mode 100644 server/configs/reload/reload_unsupported.conf create mode 100644 server/configs/reload/test.conf create mode 100644 server/reload_test.go diff --git a/server/configs/reload/invalid.conf b/server/configs/reload/invalid.conf new file mode 100644 index 00000000..ad80c5a2 --- /dev/null +++ b/server/configs/reload/invalid.conf @@ -0,0 +1,2 @@ +# Invalid config file +trace: diff --git a/server/configs/reload/reload.conf b/server/configs/reload/reload.conf new file mode 100644 index 00000000..cecfc213 --- /dev/null +++ b/server/configs/reload/reload.conf @@ -0,0 +1,42 @@ + +# Simple config file + +listen: localhost:4242 + +http: 8222 + +authorization { + user: derek + password: bella + timeout: 1 +} + +# logging options +debug: false +trace: false +logtime: false +log_file: "/tmp/gnatsd.log" +syslog: true +remote_syslog: "udp://foo.com:33" + +# pid file +pid_file: "/tmp/gnatsd.pid" + +# prof_port +prof_port: 6543 + +# max_connections +max_connections: 100 + +# maximum control line +max_control_line: 2048 + +# maximum payload +max_payload: 65536 + +# ping interval and no pong threshold +ping_interval: 60 +ping_max: 3 + +# how long server can block on a socket write to a client +write_deadline: "3s" diff --git a/server/configs/reload/reload_unsupported.conf b/server/configs/reload/reload_unsupported.conf new file mode 100644 index 00000000..9f31c4e8 --- /dev/null +++ b/server/configs/reload/reload_unsupported.conf @@ -0,0 +1,42 @@ + +# Simple config file + +listen: localhost:4242 + +http: 8222 + +authorization { + user: derek + password: bella + timeout: 1 +} + +# logging options +debug: true +trace: false +logtime: false +log_file: "/tmp/gnatsd.log" +syslog: true +remote_syslog: "udp://foo.com:33" + +# pid file +pid_file: "/tmp/gnatsd.pid" + +# prof_port +prof_port: 6543 + +# max_connections +max_connections: 100 + +# maximum control line +max_control_line: 2048 + +# maximum payload +max_payload: 65536 + +# ping interval and no pong threshold +ping_interval: 60 +ping_max: 3 + +# how long server can block on a socket write to a client +write_deadline: "3s" diff --git a/server/configs/reload/test.conf b/server/configs/reload/test.conf new file mode 100644 index 00000000..85e4f60c --- /dev/null +++ b/server/configs/reload/test.conf @@ -0,0 +1,42 @@ + +# Simple config file + +listen: localhost:4242 + +http: 8222 + +authorization { + user: derek + password: bella + timeout: 1 +} + +# logging options +debug: false +trace: true +logtime: false +log_file: "/tmp/gnatsd.log" +syslog: true +remote_syslog: "udp://foo.com:33" + +# pid file +pid_file: "/tmp/gnatsd.pid" + +# prof_port +prof_port: 6543 + +# max_connections +max_connections: 100 + +# maximum control line +max_control_line: 2048 + +# maximum payload +max_payload: 65536 + +# ping interval and no pong threshold +ping_interval: 60 +ping_max: 3 + +# how long server can block on a socket write to a client +write_deadline: "3s" diff --git a/server/reload_test.go b/server/reload_test.go new file mode 100644 index 00000000..07918243 --- /dev/null +++ b/server/reload_test.go @@ -0,0 +1,238 @@ +package server + +import ( + "os" + "path/filepath" + "reflect" + "testing" + "time" +) + +// Ensure Reload returns an error when attempting to reload a server that did +// not start with a config file. +func TestConfigReloadNoConfigFile(t *testing.T) { + server := New(&Options{}) + if server.Reload() == nil { + t.Fatal("Expected Reload to return an error") + } +} + +// Ensure Reload returns an error when attempting to change an option which +// does not support reloading. +func TestConfigReloadUnsupported(t *testing.T) { + dir, err := os.Getwd() + if err != nil { + t.Fatalf("Error getting working directory: %v", err) + } + config := filepath.Join(dir, "tmp.conf") + + golden := &Options{ + ConfigFile: config, + Host: "localhost", + Port: 4242, + Username: "derek", + Password: "bella", + AuthTimeout: 1.0, + Debug: false, + Trace: true, + Logtime: false, + HTTPPort: 8222, + LogFile: "/tmp/gnatsd.log", + PidFile: "/tmp/gnatsd.pid", + ProfPort: 6543, + Syslog: true, + RemoteSyslog: "udp://foo.com:33", + MaxControlLine: 2048, + MaxPayload: 65536, + MaxConn: 100, + PingInterval: 60 * time.Second, + MaxPingsOut: 3, + WriteDeadline: 3 * time.Second, + } + processOptions(golden) + + if err := os.Symlink("./configs/reload/test.conf", config); err != nil { + t.Fatalf("Error creating symlink: %v", err) + } + opts, err := ProcessConfigFile(config) + if err != nil { + t.Fatalf("Error processing config file: %v", err) + } + server := New(opts) + + if !reflect.DeepEqual(golden, server.getOpts()) { + t.Fatalf("Options are incorrect.\nexpected: %+v\ngot: %+v", + golden, opts) + } + + // Change config file to bad config by replacing symlink. + if err := os.Remove(config); err != nil { + t.Fatalf("Error deleting symlink: %v", err) + } + if err := os.Symlink("./configs/reload/reload_unsupported.conf", config); err != nil { + t.Fatalf("Error creating symlink: %v", err) + } + defer func() { + if err := os.Remove(config); err != nil { + t.Fatalf("Error deleting symlink: %v", err) + } + }() + + // This should fail because `debug` cannot be changed. + if err := server.Reload(); err == nil { + t.Fatal("Expected Reload to return an error") + } + + // Ensure config didn't change. + if !reflect.DeepEqual(golden, server.getOpts()) { + t.Fatalf("Options are incorrect.\nexpected: %+v\ngot: %+v", + golden, opts) + } +} + +// Ensure Reload returns an error when reloading from a bad config file. +func TestConfigReloadInvalidConfig(t *testing.T) { + dir, err := os.Getwd() + if err != nil { + t.Fatalf("Error getting working directory: %v", err) + } + config := filepath.Join(dir, "tmp.conf") + + golden := &Options{ + ConfigFile: config, + Host: "localhost", + Port: 4242, + Username: "derek", + Password: "bella", + AuthTimeout: 1.0, + Debug: false, + Trace: true, + Logtime: false, + HTTPPort: 8222, + LogFile: "/tmp/gnatsd.log", + PidFile: "/tmp/gnatsd.pid", + ProfPort: 6543, + Syslog: true, + RemoteSyslog: "udp://foo.com:33", + MaxControlLine: 2048, + MaxPayload: 65536, + MaxConn: 100, + PingInterval: 60 * time.Second, + MaxPingsOut: 3, + WriteDeadline: 3 * time.Second, + } + processOptions(golden) + + if err := os.Symlink("./configs/reload/test.conf", config); err != nil { + t.Fatalf("Error creating symlink: %v", err) + } + opts, err := ProcessConfigFile(config) + if err != nil { + t.Fatalf("Error processing config file: %v", err) + } + server := New(opts) + + if !reflect.DeepEqual(golden, server.getOpts()) { + t.Fatalf("Options are incorrect.\nexpected: %+v\ngot: %+v", + golden, opts) + } + + // Change config file to bad config by replacing symlink. + if err := os.Remove(config); err != nil { + t.Fatalf("Error deleting symlink: %v", err) + } + if err := os.Symlink("./configs/reload/invalid.conf", config); err != nil { + t.Fatalf("Error creating symlink: %v", err) + } + defer func() { + if err := os.Remove(config); err != nil { + t.Fatalf("Error deleting symlink: %v", err) + } + }() + + // This should fail because the new config should not parse. + if err := server.Reload(); err == nil { + t.Fatal("Expected Reload to return an error") + } + + // Ensure config didn't change. + if !reflect.DeepEqual(golden, server.getOpts()) { + t.Fatalf("Options are incorrect.\nexpected: %+v\ngot: %+v", + golden, opts) + } +} + +// Ensure Reload returns nil and the config is changed on success. +func TestConfigReload(t *testing.T) { + dir, err := os.Getwd() + if err != nil { + t.Fatalf("Error getting working directory: %v", err) + } + config := filepath.Join(dir, "tmp.conf") + + golden := &Options{ + ConfigFile: config, + Host: "localhost", + Port: 4242, + Username: "derek", + Password: "bella", + AuthTimeout: 1.0, + Debug: false, + Trace: true, + Logtime: false, + HTTPPort: 8222, + LogFile: "/tmp/gnatsd.log", + PidFile: "/tmp/gnatsd.pid", + ProfPort: 6543, + Syslog: true, + RemoteSyslog: "udp://foo.com:33", + MaxControlLine: 2048, + MaxPayload: 65536, + MaxConn: 100, + PingInterval: 60 * time.Second, + MaxPingsOut: 3, + WriteDeadline: 3 * time.Second, + } + processOptions(golden) + + if err := os.Symlink("./configs/reload/test.conf", config); err != nil { + t.Fatalf("Error creating symlink: %v", err) + } + opts, err := ProcessConfigFile(config) + if err != nil { + t.Fatalf("Error processing config file: %v", err) + } + server := New(opts) + + if !reflect.DeepEqual(golden, server.getOpts()) { + t.Fatalf("Options are incorrect.\nexpected: %+v\ngot: %+v", + golden, opts) + } + + // Change config file to new config by replacing symlink. + if err := os.Remove(config); err != nil { + t.Fatalf("Error deleting symlink: %v", err) + } + if err := os.Symlink("./configs/reload/reload.conf", config); err != nil { + t.Fatalf("Error creating symlink: %v", err) + } + defer func() { + if err := os.Remove(config); err != nil { + t.Fatalf("Error deleting symlink: %v", err) + } + }() + + // Should change `trace` to false. + if err := server.Reload(); err != nil { + t.Fatalf("Error reloading config: %v", err) + } + + // Ensure config changed. + var updatedGolden *Options = &Options{} + *updatedGolden = *golden + updatedGolden.Trace = false + if !reflect.DeepEqual(updatedGolden, server.getOpts()) { + t.Fatalf("Options are incorrect.\nexpected: %+v\ngot: %+v", + updatedGolden, opts) + } +}