mirror of https://github.com/status-im/consul.git
agent: Adding tests for config parsing
This commit is contained in:
parent
d7b3174804
commit
d64fda8d45
|
@ -21,69 +21,69 @@ type Config struct {
|
||||||
// AEInterval controls the anti-entropy interval. This is how often
|
// AEInterval controls the anti-entropy interval. This is how often
|
||||||
// the agent attempts to reconcile it's local state with the server'
|
// the agent attempts to reconcile it's local state with the server'
|
||||||
// representation of our state. Defaults to every 60s.
|
// representation of our state. Defaults to every 60s.
|
||||||
AEInterval time.Duration
|
AEInterval time.Duration `mapstructure:"-"`
|
||||||
|
|
||||||
// Bootstrap is used to bring up the first Consul server, and
|
// Bootstrap is used to bring up the first Consul server, and
|
||||||
// permits that node to elect itself leader
|
// permits that node to elect itself leader
|
||||||
Bootstrap bool
|
Bootstrap bool `mapstructure:"bootstrap"`
|
||||||
|
|
||||||
// Datacenter is the datacenter this node is in. Defaults to dc1
|
// Datacenter is the datacenter this node is in. Defaults to dc1
|
||||||
Datacenter string
|
Datacenter string `mapstructure:"datacenter"`
|
||||||
|
|
||||||
// DataDir is the directory to store our state in
|
// DataDir is the directory to store our state in
|
||||||
DataDir string
|
DataDir string `mapstructure:"data_dir"`
|
||||||
|
|
||||||
// DNSAddr is the address of the DNS server for the agent
|
// DNSAddr is the address of the DNS server for the agent
|
||||||
DNSAddr string
|
DNSAddr string `mapstructure:"dns_addr"`
|
||||||
|
|
||||||
// DNSRecursor can be set to allow the DNS server to recursively
|
// DNSRecursor can be set to allow the DNS server to recursively
|
||||||
// resolve non-consul domains
|
// resolve non-consul domains
|
||||||
DNSRecursor string
|
DNSRecursor string `mapstructure:"recursor"`
|
||||||
|
|
||||||
// Domain is the DNS domain for the records. Defaults to "consul."
|
// Domain is the DNS domain for the records. Defaults to "consul."
|
||||||
Domain string
|
Domain string `mapstructure:"domain"`
|
||||||
|
|
||||||
// Encryption key to use for the Serf communication
|
// Encryption key to use for the Serf communication
|
||||||
EncryptKey string
|
EncryptKey string `mapstructure:"encrypt"`
|
||||||
|
|
||||||
// HTTP interface address
|
// HTTP interface address
|
||||||
HTTPAddr string
|
HTTPAddr string `mapstructure:"http_addr"`
|
||||||
|
|
||||||
// LogLevel is the level of the logs to putout
|
// LogLevel is the level of the logs to putout
|
||||||
LogLevel string
|
LogLevel string `mapstructure:"log_level"`
|
||||||
|
|
||||||
// Node name is the name we use to advertise. Defaults to hostname.
|
// Node name is the name we use to advertise. Defaults to hostname.
|
||||||
NodeName string
|
NodeName string `mapstructure:"node_name"`
|
||||||
|
|
||||||
// RPCAddr is the address and port to listen on for the
|
// RPCAddr is the address and port to listen on for the
|
||||||
// agent's RPC interface.
|
// agent's RPC interface.
|
||||||
RPCAddr string
|
RPCAddr string `mapstructure:"rpc_addr"`
|
||||||
|
|
||||||
// BindAddr is the address that Consul's RPC and Serf's will
|
// BindAddr is the address that Consul's RPC and Serf's will
|
||||||
// bind to. This address should be routable by all other hosts.
|
// bind to. This address should be routable by all other hosts.
|
||||||
SerfBindAddr string
|
SerfBindAddr string `mapstructure:"serf_bind_addr"`
|
||||||
|
|
||||||
// SerfLanPort is the port we use for the lan-local serf cluster
|
// SerfLanPort is the port we use for the lan-local serf cluster
|
||||||
// This is used for all nodes.
|
// This is used for all nodes.
|
||||||
SerfLanPort int
|
SerfLanPort int `mapstructure:"serf_lan_port"`
|
||||||
|
|
||||||
// SerfWanPort is the port we use for the wan serf cluster.
|
// SerfWanPort is the port we use for the wan serf cluster.
|
||||||
// This is only for the Consul servers
|
// This is only for the Consul servers
|
||||||
SerfWanPort int
|
SerfWanPort int `mapstructure:"serf_wan_port"`
|
||||||
|
|
||||||
// ServerAddr is the address we use for Consul server communication.
|
// ServerAddr is the address we use for Consul server communication.
|
||||||
// Defaults to 0.0.0.0:8300
|
// Defaults to 0.0.0.0:8300
|
||||||
ServerAddr string
|
ServerAddr string `mapstructure:"server_addr"`
|
||||||
|
|
||||||
// AdvertiseAddr is the address we use for advertising our Serf,
|
// AdvertiseAddr is the address we use for advertising our Serf,
|
||||||
// and Consul RPC IP. If not specified, the first private IP we
|
// and Consul RPC IP. If not specified, the first private IP we
|
||||||
// find is used.
|
// find is used.
|
||||||
AdvertiseAddr string
|
AdvertiseAddr string `mapstructure:"advertise_addr"`
|
||||||
|
|
||||||
// Server controls if this agent acts like a Consul server,
|
// Server controls if this agent acts like a Consul server,
|
||||||
// or merely as a client. Servers have more state, take part
|
// or merely as a client. Servers have more state, take part
|
||||||
// in leader election, etc.
|
// in leader election, etc.
|
||||||
Server bool
|
Server bool `mapstructure:"server"`
|
||||||
|
|
||||||
// LeaveOnTerm controls if Serf does a graceful leave when receiving
|
// LeaveOnTerm controls if Serf does a graceful leave when receiving
|
||||||
// the TERM signal. Defaults false. This can be changed on reload.
|
// the TERM signal. Defaults false. This can be changed on reload.
|
||||||
|
@ -94,13 +94,13 @@ type Config struct {
|
||||||
SkipLeaveOnInt bool `mapstructure:"skip_leave_on_interrupt"`
|
SkipLeaveOnInt bool `mapstructure:"skip_leave_on_interrupt"`
|
||||||
|
|
||||||
// Checks holds the provided check definitions
|
// Checks holds the provided check definitions
|
||||||
Checks []*CheckDefinition
|
Checks []*CheckDefinition `mapstructure:"-"`
|
||||||
|
|
||||||
// Services holds the provided service definitions
|
// Services holds the provided service definitions
|
||||||
Services []*ServiceDefinition
|
Services []*ServiceDefinition `mapstructure:"-"`
|
||||||
|
|
||||||
// ConsulConfig can either be provided or a default one created
|
// ConsulConfig can either be provided or a default one created
|
||||||
ConsulConfig *consul.Config
|
ConsulConfig *consul.Config `mapstructure:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type dirEnts []os.FileInfo
|
type dirEnts []os.FileInfo
|
||||||
|
|
|
@ -1,3 +1,379 @@
|
||||||
package agent
|
package agent
|
||||||
|
|
||||||
// TODO: Add tests...
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConfigEncryptBytes(t *testing.T) {
|
||||||
|
// Test with some input
|
||||||
|
src := []byte("abc")
|
||||||
|
c := &Config{
|
||||||
|
EncryptKey: base64.StdEncoding.EncodeToString(src),
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := c.EncryptBytes()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(src, result) {
|
||||||
|
t.Fatalf("bad: %#v", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test with no input
|
||||||
|
c = &Config{}
|
||||||
|
result, err = c.EncryptBytes()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(result) > 0 {
|
||||||
|
t.Fatalf("bad: %#v", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeConfig(t *testing.T) {
|
||||||
|
// Basics
|
||||||
|
input := `{"data_dir": "/tmp/", "log_level": "debug"}`
|
||||||
|
config, err := DecodeConfig(bytes.NewReader([]byte(input)))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.DataDir != "/tmp/" {
|
||||||
|
t.Fatalf("bad: %#v", config)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.LogLevel != "debug" {
|
||||||
|
t.Fatalf("bad: %#v", config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Without a protocol
|
||||||
|
input = `{"node_name": "foo", "datacenter": "dc2"}`
|
||||||
|
config, err = DecodeConfig(bytes.NewReader([]byte(input)))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.NodeName != "foo" {
|
||||||
|
t.Fatalf("bad: %#v", config)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.Datacenter != "dc2" {
|
||||||
|
t.Fatalf("bad: %#v", config)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.SkipLeaveOnInt != DefaultConfig().SkipLeaveOnInt {
|
||||||
|
t.Fatalf("bad: %#v", config)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.LeaveOnTerm != DefaultConfig().LeaveOnTerm {
|
||||||
|
t.Fatalf("bad: %#v", config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server bootstrap
|
||||||
|
input = `{"server": true, "bootstrap": true}`
|
||||||
|
config, err = DecodeConfig(bytes.NewReader([]byte(input)))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !config.Server {
|
||||||
|
t.Fatalf("bad: %#v", config)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !config.Bootstrap {
|
||||||
|
t.Fatalf("bad: %#v", config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNS setup
|
||||||
|
input = `{"dns_addr": "127.0.0.1:8500", "recursor": "8.8.8.8", "domain": "foobar"}`
|
||||||
|
config, err = DecodeConfig(bytes.NewReader([]byte(input)))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.DNSAddr != "127.0.0.1:8500" {
|
||||||
|
t.Fatalf("bad: %#v", config)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.DNSRecursor != "8.8.8.8" {
|
||||||
|
t.Fatalf("bad: %#v", config)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.Domain != "foobar" {
|
||||||
|
t.Fatalf("bad: %#v", config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RPC configs
|
||||||
|
input = `{"http_addr": "127.0.0.1:1234", "rpc_addr": "127.0.0.1:8100"}`
|
||||||
|
config, err = DecodeConfig(bytes.NewReader([]byte(input)))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.HTTPAddr != "127.0.0.1:1234" {
|
||||||
|
t.Fatalf("bad: %#v", config)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.RPCAddr != "127.0.0.1:8100" {
|
||||||
|
t.Fatalf("bad: %#v", config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serf configs
|
||||||
|
input = `{"serf_bind_addr": "127.0.0.2", "serf_lan_port": 1000, "serf_wan_port": 2000}`
|
||||||
|
config, err = DecodeConfig(bytes.NewReader([]byte(input)))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.SerfBindAddr != "127.0.0.2" {
|
||||||
|
t.Fatalf("bad: %#v", config)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.SerfLanPort != 1000 {
|
||||||
|
t.Fatalf("bad: %#v", config)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.SerfWanPort != 2000 {
|
||||||
|
t.Fatalf("bad: %#v", config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server addrs
|
||||||
|
input = `{"server_addr": "127.0.0.1:8000", "advertise_addr": "127.0.0.1:8000"}`
|
||||||
|
config, err = DecodeConfig(bytes.NewReader([]byte(input)))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.ServerAddr != "127.0.0.1:8000" {
|
||||||
|
t.Fatalf("bad: %#v", config)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.AdvertiseAddr != "127.0.0.1:8000" {
|
||||||
|
t.Fatalf("bad: %#v", config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// leave_on_terminate
|
||||||
|
input = `{"leave_on_terminate": true}`
|
||||||
|
config, err = DecodeConfig(bytes.NewReader([]byte(input)))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.LeaveOnTerm != true {
|
||||||
|
t.Fatalf("bad: %#v", config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip_leave_on_interrupt
|
||||||
|
input = `{"skip_leave_on_interrupt": true}`
|
||||||
|
config, err = DecodeConfig(bytes.NewReader([]byte(input)))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.SkipLeaveOnInt != true {
|
||||||
|
t.Fatalf("bad: %#v", config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeConfig_Service(t *testing.T) {
|
||||||
|
// Basics
|
||||||
|
input := `{"service": {"id": "red1", "name": "redis", "tag": "master", "port":8000, "check": {"script": "/bin/check_redis", "interval": "10s", "ttl": "15s" }}}`
|
||||||
|
config, err := DecodeConfig(bytes.NewReader([]byte(input)))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(config.Services) != 1 {
|
||||||
|
t.Fatalf("missing service")
|
||||||
|
}
|
||||||
|
|
||||||
|
serv := config.Services[0]
|
||||||
|
if serv.ID != "red1" {
|
||||||
|
t.Fatalf("bad: %v", serv)
|
||||||
|
}
|
||||||
|
|
||||||
|
if serv.Name != "redis" {
|
||||||
|
t.Fatalf("bad: %v", serv)
|
||||||
|
}
|
||||||
|
|
||||||
|
if serv.Tag != "master" {
|
||||||
|
t.Fatalf("bad: %v", serv)
|
||||||
|
}
|
||||||
|
|
||||||
|
if serv.Port != 8000 {
|
||||||
|
t.Fatalf("bad: %v", serv)
|
||||||
|
}
|
||||||
|
|
||||||
|
if serv.Check.Script != "/bin/check_redis" {
|
||||||
|
t.Fatalf("bad: %v", serv)
|
||||||
|
}
|
||||||
|
|
||||||
|
if serv.Check.Interval != 10*time.Second {
|
||||||
|
t.Fatalf("bad: %v", serv)
|
||||||
|
}
|
||||||
|
|
||||||
|
if serv.Check.TTL != 15*time.Second {
|
||||||
|
t.Fatalf("bad: %v", serv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeConfig_Check(t *testing.T) {
|
||||||
|
// Basics
|
||||||
|
input := `{"check": {"id": "chk1", "name": "mem", "notes": "foobar", "script": "/bin/check_redis", "interval": "10s", "ttl": "15s" }}`
|
||||||
|
config, err := DecodeConfig(bytes.NewReader([]byte(input)))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(config.Checks) != 1 {
|
||||||
|
t.Fatalf("missing check")
|
||||||
|
}
|
||||||
|
|
||||||
|
chk := config.Checks[0]
|
||||||
|
if chk.ID != "chk1" {
|
||||||
|
t.Fatalf("bad: %v", chk)
|
||||||
|
}
|
||||||
|
|
||||||
|
if chk.Name != "mem" {
|
||||||
|
t.Fatalf("bad: %v", chk)
|
||||||
|
}
|
||||||
|
|
||||||
|
if chk.Notes != "foobar" {
|
||||||
|
t.Fatalf("bad: %v", chk)
|
||||||
|
}
|
||||||
|
|
||||||
|
if chk.Script != "/bin/check_redis" {
|
||||||
|
t.Fatalf("bad: %v", chk)
|
||||||
|
}
|
||||||
|
|
||||||
|
if chk.Interval != 10*time.Second {
|
||||||
|
t.Fatalf("bad: %v", chk)
|
||||||
|
}
|
||||||
|
|
||||||
|
if chk.TTL != 15*time.Second {
|
||||||
|
t.Fatalf("bad: %v", chk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeConfig(t *testing.T) {
|
||||||
|
a := &Config{
|
||||||
|
Bootstrap: false,
|
||||||
|
Datacenter: "dc1",
|
||||||
|
DataDir: "/tmp/foo",
|
||||||
|
DNSAddr: "127.0.0.1:1000",
|
||||||
|
DNSRecursor: "127.0.0.1:1001",
|
||||||
|
Domain: "basic",
|
||||||
|
HTTPAddr: "",
|
||||||
|
LogLevel: "debug",
|
||||||
|
NodeName: "foo",
|
||||||
|
RPCAddr: "",
|
||||||
|
SerfBindAddr: "127.0.0.1",
|
||||||
|
SerfLanPort: 1000,
|
||||||
|
SerfWanPort: 2000,
|
||||||
|
ServerAddr: "127.0.0.1:8000",
|
||||||
|
AdvertiseAddr: "127.0.0.1:8000",
|
||||||
|
Server: false,
|
||||||
|
LeaveOnTerm: false,
|
||||||
|
SkipLeaveOnInt: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
b := &Config{
|
||||||
|
Bootstrap: true,
|
||||||
|
Datacenter: "dc2",
|
||||||
|
DataDir: "/tmp/bar",
|
||||||
|
DNSAddr: "127.0.0.2:1000",
|
||||||
|
DNSRecursor: "127.0.0.2:1001",
|
||||||
|
Domain: "other",
|
||||||
|
HTTPAddr: "127.0.0.1:12345",
|
||||||
|
LogLevel: "info",
|
||||||
|
NodeName: "baz",
|
||||||
|
RPCAddr: "127.0.0.1:9999",
|
||||||
|
SerfBindAddr: "127.0.0.2",
|
||||||
|
SerfLanPort: 3000,
|
||||||
|
SerfWanPort: 4000,
|
||||||
|
ServerAddr: "127.0.0.2:8000",
|
||||||
|
AdvertiseAddr: "127.0.0.2:8000",
|
||||||
|
Server: true,
|
||||||
|
LeaveOnTerm: true,
|
||||||
|
SkipLeaveOnInt: true,
|
||||||
|
Checks: []*CheckDefinition{nil},
|
||||||
|
Services: []*ServiceDefinition{nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
c := MergeConfig(a, b)
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(c, b) {
|
||||||
|
t.Fatalf("should be equal %v %v", c, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadConfigPaths_badPath(t *testing.T) {
|
||||||
|
_, err := ReadConfigPaths([]string{"/i/shouldnt/exist/ever/rainbows"})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("should have err")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadConfigPaths_file(t *testing.T) {
|
||||||
|
tf, err := ioutil.TempFile("", "consul")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
tf.Write([]byte(`{"node_name":"bar"}`))
|
||||||
|
tf.Close()
|
||||||
|
defer os.Remove(tf.Name())
|
||||||
|
|
||||||
|
config, err := ReadConfigPaths([]string{tf.Name()})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.NodeName != "bar" {
|
||||||
|
t.Fatalf("bad: %#v", config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadConfigPaths_dir(t *testing.T) {
|
||||||
|
td, err := ioutil.TempDir("", "consul")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(td)
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(filepath.Join(td, "a.json"),
|
||||||
|
[]byte(`{"node_name": "bar"}`), 0644)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(filepath.Join(td, "b.json"),
|
||||||
|
[]byte(`{"node_name": "baz"}`), 0644)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A non-json file, shouldn't be read
|
||||||
|
err = ioutil.WriteFile(filepath.Join(td, "c"),
|
||||||
|
[]byte(`{"node_name": "bad"}`), 0644)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config, err := ReadConfigPaths([]string{td})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.NodeName != "baz" {
|
||||||
|
t.Fatalf("bad: %#v", config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue