mirror of https://github.com/hashicorp/consul
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1812 lines
43 KiB
1812 lines
43 KiB
package agent |
|
|
|
import ( |
|
"bytes" |
|
"encoding/base64" |
|
"io/ioutil" |
|
"net" |
|
"os" |
|
"path/filepath" |
|
"reflect" |
|
"strings" |
|
"testing" |
|
"time" |
|
|
|
"github.com/hashicorp/consul/lib" |
|
) |
|
|
|
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_id": "bar", "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.NodeID != "bar" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
if config.Datacenter != "dc2" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
if config.SkipLeaveOnInt != nil { |
|
t.Fatalf("bad: expected nil SkipLeaveOnInt") |
|
} |
|
|
|
if config.LeaveOnTerm != nil { |
|
t.Fatalf("bad: expected nil LeaveOnTerm") |
|
} |
|
|
|
// 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) |
|
} |
|
|
|
// Expect bootstrap |
|
input = `{"server": true, "bootstrap_expect": 3}` |
|
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.BootstrapExpect != 3 { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// DNS setup |
|
input = `{"ports": {"dns": 8500}, "recursors": ["8.8.8.8","8.8.4.4"], "recursor":"127.0.0.1", "domain": "foobar"}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if config.Ports.DNS != 8500 { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
if len(config.DNSRecursors) != 3 { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.DNSRecursors[0] != "8.8.8.8" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.DNSRecursors[1] != "8.8.4.4" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.DNSRecursors[2] != "127.0.0.1" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
if config.Domain != "foobar" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// RPC configs |
|
input = `{"ports": {"http": 1234, "https": 1243}, "client_addr": "0.0.0.0"}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if config.ClientAddr != "0.0.0.0" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
if config.Ports.HTTP != 1234 { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
if config.Ports.HTTPS != 1243 { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// Serf configs |
|
input = `{"ports": {"serf_lan": 1000, "serf_wan": 2000}}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if config.Ports.SerfLan != 1000 { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
if config.Ports.SerfWan != 2000 { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// Server addrs |
|
input = `{"ports": {"server": 8000}, "bind_addr": "127.0.0.2", "advertise_addr": "127.0.0.3", "serf_lan_bind": "127.0.0.4", "serf_wan_bind": "52.54.55.56"}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
|
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if config.BindAddr != "127.0.0.2" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
if config.SerfWanBindAddr != "52.54.55.56" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
if config.SerfLanBindAddr != "127.0.0.4" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
if config.AdvertiseAddr != "127.0.0.3" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
if config.AdvertiseAddrWan != "" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
if config.Ports.Server != 8000 { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// Advertise address for wan |
|
input = `{"advertise_addr_wan": "127.0.0.5"}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
if config.AdvertiseAddr != "" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.AdvertiseAddrWan != "127.0.0.5" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// Advertise addresses for serflan |
|
input = `{"advertise_addrs": {"serf_lan": "127.0.0.5:1234"}}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
if config.AdvertiseAddrs.SerfLanRaw != "127.0.0.5:1234" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.AdvertiseAddrs.SerfLan.String() != "127.0.0.5:1234" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// Advertise addresses for serfwan |
|
input = `{"advertise_addrs": {"serf_wan": "127.0.0.5:1234"}}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
if config.AdvertiseAddrs.SerfWanRaw != "127.0.0.5:1234" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.AdvertiseAddrs.SerfWan.String() != "127.0.0.5:1234" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// Advertise addresses for rpc |
|
input = `{"advertise_addrs": {"rpc": "127.0.0.5:1234"}}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
if config.AdvertiseAddrs.RPCRaw != "127.0.0.5:1234" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.AdvertiseAddrs.RPC.String() != "127.0.0.5:1234" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// WAN address translation disabled by default |
|
config, err = DecodeConfig(bytes.NewReader([]byte(`{}`))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
if config.TranslateWanAddrs != false { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// WAN address translation |
|
input = `{"translate_wan_addrs": true}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
if config.TranslateWanAddrs != true { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// raft protocol |
|
input = `{"raft_protocol": 3}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if config.RaftProtocol != 3 { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// Node metadata fields |
|
input = `{"node_meta": {"thing1": "1", "thing2": "2"}}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
if v, ok := config.Meta["thing1"]; !ok || v != "1" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if v, ok := config.Meta["thing2"]; !ok || v != "2" { |
|
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) |
|
} |
|
|
|
// enable_debug |
|
input = `{"enable_debug": true}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if config.EnableDebug != true { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// TLS |
|
input = `{"verify_incoming": true, "verify_outgoing": true, "verify_server_hostname": true, "tls_min_version": "tls12"}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if config.VerifyIncoming != true { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
if config.VerifyOutgoing != true { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
if config.VerifyServerHostname != true { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
if config.TLSMinVersion != "tls12" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// TLS keys |
|
input = `{"ca_file": "my/ca/file", "cert_file": "my.cert", "key_file": "key.pem", "server_name": "example.com"}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if config.CAFile != "my/ca/file" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.CertFile != "my.cert" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.KeyFile != "key.pem" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.ServerName != "example.com" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// Start join |
|
input = `{"start_join": ["1.1.1.1", "2.2.2.2"]}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if len(config.StartJoin) != 2 { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.StartJoin[0] != "1.1.1.1" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.StartJoin[1] != "2.2.2.2" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// Start Join wan |
|
input = `{"start_join_wan": ["1.1.1.1", "2.2.2.2"]}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if len(config.StartJoinWan) != 2 { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.StartJoinWan[0] != "1.1.1.1" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.StartJoinWan[1] != "2.2.2.2" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// Retry join |
|
input = `{"retry_join": ["1.1.1.1", "2.2.2.2"]}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if len(config.RetryJoin) != 2 { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.RetryJoin[0] != "1.1.1.1" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.RetryJoin[1] != "2.2.2.2" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// Retry interval |
|
input = `{"retry_interval": "10s"}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if config.RetryIntervalRaw != "10s" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.RetryInterval.String() != "10s" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// Retry Max |
|
input = `{"retry_max": 3}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if config.RetryMaxAttempts != 3 { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// Retry Join wan |
|
input = `{"retry_join_wan": ["1.1.1.1", "2.2.2.2"]}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if len(config.RetryJoinWan) != 2 { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.RetryJoinWan[0] != "1.1.1.1" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.RetryJoinWan[1] != "2.2.2.2" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// Retry Interval wan |
|
input = `{"retry_interval_wan": "10s"}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if config.RetryIntervalWanRaw != "10s" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.RetryIntervalWan.String() != "10s" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// Retry Max wan |
|
input = `{"retry_max_wan": 3}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if config.RetryMaxAttemptsWan != 3 { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// Reconnect timeout LAN and WAN |
|
input = `{"reconnect_timeout": "8h", "reconnect_timeout_wan": "10h"}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
if config.ReconnectTimeoutLanRaw != "8h" || |
|
config.ReconnectTimeoutLan.String() != "8h0m0s" || |
|
config.ReconnectTimeoutWanRaw != "10h" || |
|
config.ReconnectTimeoutWan.String() != "10h0m0s" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
input = `{"reconnect_timeout": "7h"}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err == nil { |
|
t.Fatalf("decode should have failed") |
|
} |
|
input = `{"reconnect_timeout_wan": "7h"}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err == nil { |
|
t.Fatalf("decode should have failed") |
|
} |
|
|
|
// Static UI server |
|
input = `{"ui": true}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if !config.EnableUi { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// UI Dir |
|
input = `{"ui_dir": "/opt/consul-ui"}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if config.UiDir != "/opt/consul-ui" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// Pid File |
|
input = `{"pid_file": "/tmp/consul/pid"}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if config.PidFile != "/tmp/consul/pid" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// Syslog |
|
input = `{"enable_syslog": true, "syslog_facility": "LOCAL4"}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if !config.EnableSyslog { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.SyslogFacility != "LOCAL4" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// Rejoin |
|
input = `{"rejoin_after_leave": true}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if !config.RejoinAfterLeave { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// DNS node ttl, max stale |
|
input = `{"dns_config": {"allow_stale": false, "enable_truncate": false, "max_stale": "15s", "node_ttl": "5s", "only_passing": true, "udp_answer_limit": 6, "recursor_timeout": "7s"}}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if *config.DNSConfig.AllowStale { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.DNSConfig.EnableTruncate { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.DNSConfig.MaxStale != 15*time.Second { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.DNSConfig.NodeTTL != 5*time.Second { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if !config.DNSConfig.OnlyPassing { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.DNSConfig.UDPAnswerLimit != 6 { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.DNSConfig.RecursorTimeout != 7*time.Second { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// DNS service ttl |
|
input = `{"dns_config": {"service_ttl": {"*": "1s", "api": "10s", "web": "30s"}}}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if config.DNSConfig.ServiceTTL["*"] != time.Second { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.DNSConfig.ServiceTTL["api"] != 10*time.Second { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.DNSConfig.ServiceTTL["web"] != 30*time.Second { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// DNS enable truncate |
|
input = `{"dns_config": {"enable_truncate": true}}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if !config.DNSConfig.EnableTruncate { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// DNS only passing |
|
input = `{"dns_config": {"only_passing": true}}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if !config.DNSConfig.OnlyPassing { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// DNS disable compression |
|
input = `{"dns_config": {"disable_compression": true}}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if !config.DNSConfig.DisableCompression { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// CheckUpdateInterval |
|
input = `{"check_update_interval": "10m"}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if config.CheckUpdateInterval != 10*time.Minute { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// ACLs |
|
input = `{"acl_token": "1111", "acl_agent_master_token": "2222", |
|
"acl_agent_token": "3333", "acl_datacenter": "dc2", |
|
"acl_ttl": "60s", "acl_down_policy": "deny", |
|
"acl_default_policy": "deny", "acl_master_token": "2345", |
|
"acl_replication_token": "8675309"}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if config.ACLToken != "1111" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.ACLAgentMasterToken != "2222" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.ACLAgentToken != "3333" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.ACLMasterToken != "2345" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.ACLDatacenter != "dc2" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.ACLTTL != 60*time.Second { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.ACLDownPolicy != "deny" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.ACLDefaultPolicy != "deny" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.ACLReplicationToken != "8675309" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// ACL token precedence. |
|
input = `{}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
if token := config.GetTokenForAgent(); token != "" { |
|
t.Fatalf("bad: %s", token) |
|
} |
|
input = `{"acl_token": "hello"}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
if token := config.GetTokenForAgent(); token != "hello" { |
|
t.Fatalf("bad: %s", token) |
|
} |
|
input = `{"acl_agent_token": "world", "acl_token": "hello"}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
if token := config.GetTokenForAgent(); token != "world" { |
|
t.Fatalf("bad: %s", token) |
|
} |
|
|
|
// ACL flag for Consul version 0.8 features (broken out since we will |
|
// eventually remove this). We first verify this is opt-out. |
|
config = DefaultConfig() |
|
if *config.ACLEnforceVersion8 != false { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
input = `{"acl_enforce_version_8": true}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
if *config.ACLEnforceVersion8 != true { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// Watches |
|
input = `{"watches": [{"type":"keyprefix", "prefix":"foo/", "handler":"foobar"}]}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if len(config.Watches) != 1 { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
out := config.Watches[0] |
|
exp := map[string]interface{}{ |
|
"type": "keyprefix", |
|
"prefix": "foo/", |
|
"handler": "foobar", |
|
} |
|
if !reflect.DeepEqual(out, exp) { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// remote exec |
|
input = `{"disable_remote_exec": true}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if !config.DisableRemoteExec { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// stats(d|ite) exec |
|
input = `{"statsite_addr": "127.0.0.1:7250", "statsd_addr": "127.0.0.1:7251"}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if config.Telemetry.StatsiteAddr != "127.0.0.1:7250" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.Telemetry.StatsdAddr != "127.0.0.1:7251" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// dogstatsd |
|
input = `{"dogstatsd_addr": "127.0.0.1:7254", "dogstatsd_tags":["tag_1:val_1", "tag_2:val_2"]}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
if config.Telemetry.DogStatsdAddr != "127.0.0.1:7254" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
if len(config.Telemetry.DogStatsdTags) != 2 { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
if config.Telemetry.DogStatsdTags[0] != "tag_1:val_1" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
if config.Telemetry.DogStatsdTags[1] != "tag_2:val_2" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// Statsite prefix |
|
input = `{"statsite_prefix": "my_prefix"}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
if config.Telemetry.StatsitePrefix != "my_prefix" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// Circonus settings |
|
input = `{"telemetry": {"circonus_api_token": "12345678-1234-1234-12345678", "circonus_api_app": "testApp", |
|
"circonus_api_url": "https://api.host.foo/v2", "circonus_submission_interval": "15s", |
|
"circonus_submission_url": "https://submit.host.bar:123/one/two/three", |
|
"circonus_check_id": "12345", "circonus_check_force_metric_activation": "true", |
|
"circonus_check_instance_id": "a:b", "circonus_check_search_tag": "c:d", |
|
"circonus_check_display_name": "node1:consul", "circonus_check_tags": "cat1:tag1,cat2:tag2", |
|
"circonus_broker_id": "6789", "circonus_broker_select_tag": "e:f"} }` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
if config.Telemetry.CirconusAPIToken != "12345678-1234-1234-12345678" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.Telemetry.CirconusAPIApp != "testApp" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.Telemetry.CirconusAPIURL != "https://api.host.foo/v2" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.Telemetry.CirconusSubmissionInterval != "15s" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.Telemetry.CirconusCheckSubmissionURL != "https://submit.host.bar:123/one/two/three" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.Telemetry.CirconusCheckID != "12345" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.Telemetry.CirconusCheckForceMetricActivation != "true" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.Telemetry.CirconusCheckInstanceID != "a:b" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.Telemetry.CirconusCheckSearchTag != "c:d" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.Telemetry.CirconusCheckDisplayName != "node1:consul" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.Telemetry.CirconusCheckTags != "cat1:tag1,cat2:tag2" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.Telemetry.CirconusBrokerID != "6789" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.Telemetry.CirconusBrokerSelectTag != "e:f" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// New telemetry |
|
input = `{"telemetry": { "statsite_prefix": "my_prefix", "statsite_address": "127.0.0.1:7250", "statsd_address":"127.0.0.1:7251", "disable_hostname": true, "dogstatsd_addr": "1.1.1.1:111", "dogstatsd_tags": [ "tag_1:val_1" ] } }` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
if config.Telemetry.StatsitePrefix != "my_prefix" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.Telemetry.StatsiteAddr != "127.0.0.1:7250" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.Telemetry.StatsdAddr != "127.0.0.1:7251" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.Telemetry.DisableHostname != true { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.Telemetry.DogStatsdAddr != "1.1.1.1:111" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.Telemetry.DogStatsdTags[0] != "tag_1:val_1" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// Address overrides |
|
input = `{"addresses": {"dns": "0.0.0.0", "http": "127.0.0.1", "https": "127.0.0.1"}}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if config.Addresses.DNS != "0.0.0.0" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.Addresses.HTTP != "127.0.0.1" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.Addresses.HTTPS != "127.0.0.1" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// Domain socket permissions |
|
input = `{"unix_sockets": {"user": "500", "group": "500", "mode": "0700"}}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if config.UnixSockets.Usr != "500" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.UnixSockets.Grp != "500" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.UnixSockets.Perms != "0700" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// Disable updates |
|
input = `{"disable_update_check": true, "disable_anonymous_signature": true}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if !config.DisableUpdateCheck { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if !config.DisableAnonymousSignature { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// HTTP API response header fields |
|
input = `{"http_api_response_headers": {"Access-Control-Allow-Origin": "*", "X-XSS-Protection": "1; mode=block"}}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if config.HTTPAPIResponseHeaders["Access-Control-Allow-Origin"] != "*" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
if config.HTTPAPIResponseHeaders["X-XSS-Protection"] != "1; mode=block" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// Atlas configs |
|
input = `{ |
|
"atlas_infrastructure": "hashicorp/prod", |
|
"atlas_token": "abcdefg", |
|
"atlas_acl_token": "123456789", |
|
"atlas_join": true, |
|
"atlas_endpoint": "foo.bar:1111" |
|
}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if config.AtlasInfrastructure != "hashicorp/prod" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.AtlasToken != "abcdefg" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.AtlasACLToken != "123456789" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if !config.AtlasJoin { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.AtlasEndpoint != "foo.bar:1111" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
|
|
// Coordinate disable |
|
input = `{"disable_coordinates": true}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if config.DisableCoordinates != true { |
|
t.Fatalf("bad: coordinates not disabled: %#v", config) |
|
} |
|
|
|
// SessionTTLMin |
|
input = `{"session_ttl_min": "5s"}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if config.SessionTTLMin != 5*time.Second { |
|
t.Fatalf("bad: %s %#v", config.SessionTTLMin.String(), config) |
|
} |
|
} |
|
|
|
func TestDecodeConfig_invalidKeys(t *testing.T) { |
|
input := `{"bad": "no way jose"}` |
|
_, err := DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err == nil || !strings.Contains(err.Error(), "invalid keys") { |
|
t.Fatalf("should have rejected invalid config keys") |
|
} |
|
} |
|
|
|
func TestRetryJoinEC2(t *testing.T) { |
|
input := `{"retry_join_ec2": { |
|
"region": "us-east-1", |
|
"tag_key": "ConsulRole", |
|
"tag_value": "Server", |
|
"access_key_id": "asdf", |
|
"secret_access_key": "qwerty" |
|
}}` |
|
config, err := DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if config.RetryJoinEC2.Region != "us-east-1" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.RetryJoinEC2.TagKey != "ConsulRole" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.RetryJoinEC2.TagValue != "Server" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.RetryJoinEC2.AccessKeyID != "asdf" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.RetryJoinEC2.SecretAccessKey != "qwerty" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
} |
|
|
|
func TestRetryJoinGCE(t *testing.T) { |
|
input := `{"retry_join_gce": { |
|
"project_name": "test-project", |
|
"zone_pattern": "us-west1-a", |
|
"tag_value": "consul-server", |
|
"credentials_file": "/path/to/foo.json" |
|
}}` |
|
config, err := DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
if config.RetryJoinGCE.ProjectName != "test-project" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.RetryJoinGCE.ZonePattern != "us-west1-a" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.RetryJoinGCE.TagValue != "consul-server" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
if config.RetryJoinGCE.CredentialsFile != "/path/to/foo.json" { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
} |
|
|
|
func TestDecodeConfig_Performance(t *testing.T) { |
|
input := `{"performance": { "raft_multiplier": 3 }}` |
|
config, err := DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
if config.Performance.RaftMultiplier != 3 { |
|
t.Fatalf("bad: multiplier isn't set: %#v", config) |
|
} |
|
|
|
input = `{"performance": { "raft_multiplier": 11 }}` |
|
config, err = DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err == nil || !strings.Contains(err.Error(), "Performance.RaftMultiplier must be <=") { |
|
t.Fatalf("bad: %v", err) |
|
} |
|
} |
|
|
|
func TestDecodeConfig_Autopilot(t *testing.T) { |
|
input := `{"autopilot": { "cleanup_dead_servers": true }}` |
|
config, err := DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
if config.Autopilot.CleanupDeadServers == nil || !*config.Autopilot.CleanupDeadServers { |
|
t.Fatalf("bad: cleanup_dead_servers isn't set: %#v", config) |
|
} |
|
} |
|
|
|
func TestDecodeConfig_Services(t *testing.T) { |
|
input := `{ |
|
"services": [ |
|
{ |
|
"id": "red0", |
|
"name": "redis", |
|
"tags": [ |
|
"master" |
|
], |
|
"port": 6000, |
|
"check": { |
|
"script": "/bin/check_redis -p 6000", |
|
"interval": "5s", |
|
"ttl": "20s" |
|
}, |
|
"checks": [ |
|
{ |
|
"script": "/bin/check_redis_read", |
|
"interval": "1m" |
|
}, |
|
{ |
|
"script": "/bin/check_redis_write", |
|
"interval": "1m" |
|
} |
|
] |
|
}, |
|
{ |
|
"id": "red1", |
|
"name": "redis", |
|
"tags": [ |
|
"delayed", |
|
"slave" |
|
], |
|
"port": 7000, |
|
"check": { |
|
"script": "/bin/check_redis -p 7000", |
|
"interval": "30s", |
|
"ttl": "60s" |
|
} |
|
}, |
|
{ |
|
"id": "es0", |
|
"name": "elasticsearch", |
|
"port": 9200, |
|
"check": { |
|
"HTTP": "http://localhost:9200/_cluster_health", |
|
"interval": "10s", |
|
"timeout": "100ms" |
|
} |
|
} |
|
] |
|
}` |
|
|
|
config, err := DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
expected := &Config{ |
|
Services: []*ServiceDefinition{ |
|
&ServiceDefinition{ |
|
Check: CheckType{ |
|
Interval: 5 * time.Second, |
|
Script: "/bin/check_redis -p 6000", |
|
TTL: 20 * time.Second, |
|
}, |
|
Checks: CheckTypes{ |
|
&CheckType{ |
|
Interval: time.Minute, |
|
Script: "/bin/check_redis_read", |
|
}, |
|
&CheckType{ |
|
Interval: time.Minute, |
|
Script: "/bin/check_redis_write", |
|
}, |
|
}, |
|
ID: "red0", |
|
Name: "redis", |
|
Tags: []string{ |
|
"master", |
|
}, |
|
Port: 6000, |
|
}, |
|
&ServiceDefinition{ |
|
Check: CheckType{ |
|
Interval: 30 * time.Second, |
|
Script: "/bin/check_redis -p 7000", |
|
TTL: 60 * time.Second, |
|
}, |
|
ID: "red1", |
|
Name: "redis", |
|
Tags: []string{ |
|
"delayed", |
|
"slave", |
|
}, |
|
Port: 7000, |
|
}, |
|
&ServiceDefinition{ |
|
Check: CheckType{ |
|
HTTP: "http://localhost:9200/_cluster_health", |
|
Interval: 10 * time.Second, |
|
Timeout: 100 * time.Millisecond, |
|
}, |
|
ID: "es0", |
|
Name: "elasticsearch", |
|
Port: 9200, |
|
}, |
|
}, |
|
} |
|
|
|
if !reflect.DeepEqual(config, expected) { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
} |
|
|
|
func TestDecodeConfig_verifyUniqueListeners(t *testing.T) { |
|
tests := []struct { |
|
name string |
|
cfg string |
|
pass bool |
|
}{ |
|
{ |
|
"http_dns1", |
|
`{"addresses": {"http": "0.0.0.0", "dns": "127.0.0.1"}, "ports": {"dns": 8000}}`, |
|
true, |
|
}, |
|
{ |
|
"http_dns IP identical", |
|
`{"addresses": {"http": "0.0.0.0", "dns": "0.0.0.0"}, "ports": {"http": 8000, "dns": 8000}}`, |
|
false, |
|
}, |
|
} |
|
|
|
for _, test := range tests { |
|
config, err := DecodeConfig(bytes.NewReader([]byte(test.cfg))) |
|
if err != nil { |
|
t.Fatalf("err: %s %s", test.name, err) |
|
} |
|
|
|
err = config.verifyUniqueListeners() |
|
if (err != nil && test.pass) || (err == nil && !test.pass) { |
|
t.Errorf("err: %s should have %v: %v: %v", test.name, test.pass, test.cfg, err) |
|
} |
|
} |
|
} |
|
|
|
func TestDecodeConfig_Checks(t *testing.T) { |
|
input := `{ |
|
"checks": [ |
|
{ |
|
"id": "chk1", |
|
"name": "mem", |
|
"script": "/bin/check_mem", |
|
"interval": "5s" |
|
}, |
|
{ |
|
"id": "chk2", |
|
"name": "cpu", |
|
"script": "/bin/check_cpu", |
|
"interval": "10s" |
|
}, |
|
{ |
|
"id": "chk3", |
|
"name": "service:redis:tx", |
|
"script": "/bin/check_redis_tx", |
|
"interval": "1m", |
|
"service_id": "redis" |
|
}, |
|
{ |
|
"id": "chk4", |
|
"name": "service:elasticsearch:health", |
|
"HTTP": "http://localhost:9200/_cluster_health", |
|
"interval": "10s", |
|
"timeout": "100ms", |
|
"service_id": "elasticsearch" |
|
}, |
|
{ |
|
"id": "chk5", |
|
"name": "service:sslservice", |
|
"HTTP": "https://sslservice/status", |
|
"interval": "10s", |
|
"timeout": "100ms", |
|
"service_id": "sslservice" |
|
}, |
|
{ |
|
"id": "chk6", |
|
"name": "service:insecure-sslservice", |
|
"HTTP": "https://insecure-sslservice/status", |
|
"interval": "10s", |
|
"timeout": "100ms", |
|
"service_id": "insecure-sslservice", |
|
"tls_skip_verify": true |
|
} |
|
] |
|
}` |
|
|
|
config, err := DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
expected := &Config{ |
|
Checks: []*CheckDefinition{ |
|
&CheckDefinition{ |
|
ID: "chk1", |
|
Name: "mem", |
|
CheckType: CheckType{ |
|
Script: "/bin/check_mem", |
|
Interval: 5 * time.Second, |
|
}, |
|
}, |
|
&CheckDefinition{ |
|
ID: "chk2", |
|
Name: "cpu", |
|
CheckType: CheckType{ |
|
Script: "/bin/check_cpu", |
|
Interval: 10 * time.Second, |
|
}, |
|
}, |
|
&CheckDefinition{ |
|
ID: "chk3", |
|
Name: "service:redis:tx", |
|
ServiceID: "redis", |
|
CheckType: CheckType{ |
|
Script: "/bin/check_redis_tx", |
|
Interval: time.Minute, |
|
}, |
|
}, |
|
&CheckDefinition{ |
|
ID: "chk4", |
|
Name: "service:elasticsearch:health", |
|
ServiceID: "elasticsearch", |
|
CheckType: CheckType{ |
|
HTTP: "http://localhost:9200/_cluster_health", |
|
Interval: 10 * time.Second, |
|
Timeout: 100 * time.Millisecond, |
|
}, |
|
}, |
|
&CheckDefinition{ |
|
ID: "chk5", |
|
Name: "service:sslservice", |
|
ServiceID: "sslservice", |
|
CheckType: CheckType{ |
|
HTTP: "https://sslservice/status", |
|
Interval: 10 * time.Second, |
|
Timeout: 100 * time.Millisecond, |
|
TLSSkipVerify: false, |
|
}, |
|
}, |
|
&CheckDefinition{ |
|
ID: "chk6", |
|
Name: "service:insecure-sslservice", |
|
ServiceID: "insecure-sslservice", |
|
CheckType: CheckType{ |
|
HTTP: "https://insecure-sslservice/status", |
|
Interval: 10 * time.Second, |
|
Timeout: 100 * time.Millisecond, |
|
TLSSkipVerify: true, |
|
}, |
|
}, |
|
}, |
|
} |
|
|
|
if !reflect.DeepEqual(config, expected) { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
} |
|
|
|
func TestDecodeConfig_Multiples(t *testing.T) { |
|
input := `{ |
|
"services": [ |
|
{ |
|
"id": "red0", |
|
"name": "redis", |
|
"tags": [ |
|
"master" |
|
], |
|
"port": 6000, |
|
"check": { |
|
"script": "/bin/check_redis -p 6000", |
|
"interval": "5s", |
|
"ttl": "20s" |
|
} |
|
} |
|
], |
|
"checks": [ |
|
{ |
|
"id": "chk1", |
|
"name": "mem", |
|
"script": "/bin/check_mem", |
|
"interval": "10s" |
|
} |
|
] |
|
}` |
|
|
|
config, err := DecodeConfig(bytes.NewReader([]byte(input))) |
|
if err != nil { |
|
t.Fatalf("err: %s", err) |
|
} |
|
|
|
expected := &Config{ |
|
Services: []*ServiceDefinition{ |
|
&ServiceDefinition{ |
|
Check: CheckType{ |
|
Interval: 5 * time.Second, |
|
Script: "/bin/check_redis -p 6000", |
|
TTL: 20 * time.Second, |
|
}, |
|
ID: "red0", |
|
Name: "redis", |
|
Tags: []string{ |
|
"master", |
|
}, |
|
Port: 6000, |
|
}, |
|
}, |
|
Checks: []*CheckDefinition{ |
|
&CheckDefinition{ |
|
ID: "chk1", |
|
Name: "mem", |
|
CheckType: CheckType{ |
|
Script: "/bin/check_mem", |
|
Interval: 10 * time.Second, |
|
}, |
|
}, |
|
}, |
|
} |
|
|
|
if !reflect.DeepEqual(config, expected) { |
|
t.Fatalf("bad: %#v", config) |
|
} |
|
} |
|
|
|
func TestDecodeConfig_Service(t *testing.T) { |
|
// Basics |
|
input := `{"service": {"id": "red1", "name": "redis", "tags": ["master"], "port":8000, "check": {"script": "/bin/check_redis", "interval": "10s", "ttl": "15s", "DeregisterCriticalServiceAfter": "90m" }}}` |
|
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 !lib.StrContains(serv.Tags, "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) |
|
} |
|
|
|
if serv.Check.DeregisterCriticalServiceAfter != 90*time.Minute { |
|
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", "shell": "/bin/bash", "docker_container_id": "redis", "deregister_critical_service_after": "90s" }}` |
|
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) |
|
} |
|
|
|
if chk.Shell != "/bin/bash" { |
|
t.Fatalf("bad: %v", chk) |
|
} |
|
|
|
if chk.DockerContainerID != "redis" { |
|
t.Fatalf("bad: %v", chk) |
|
} |
|
|
|
if chk.DeregisterCriticalServiceAfter != 90*time.Second { |
|
t.Fatalf("bad: %v", chk) |
|
} |
|
} |
|
|
|
func TestMergeConfig(t *testing.T) { |
|
a := &Config{ |
|
Bootstrap: false, |
|
BootstrapExpect: 0, |
|
Datacenter: "dc1", |
|
DataDir: "/tmp/foo", |
|
Domain: "basic", |
|
LogLevel: "debug", |
|
NodeID: "bar", |
|
NodeName: "foo", |
|
ClientAddr: "127.0.0.1", |
|
BindAddr: "127.0.0.1", |
|
AdvertiseAddr: "127.0.0.1", |
|
Server: false, |
|
LeaveOnTerm: new(bool), |
|
SkipLeaveOnInt: new(bool), |
|
EnableDebug: false, |
|
CheckUpdateIntervalRaw: "8m", |
|
RetryIntervalRaw: "10s", |
|
RetryIntervalWanRaw: "10s", |
|
RetryJoinEC2: RetryJoinEC2{ |
|
Region: "us-east-1", |
|
TagKey: "Key1", |
|
TagValue: "Value1", |
|
AccessKeyID: "nope", |
|
SecretAccessKey: "nope", |
|
}, |
|
Telemetry: Telemetry{ |
|
DisableHostname: false, |
|
StatsdAddr: "nope", |
|
StatsiteAddr: "nope", |
|
StatsitePrefix: "nope", |
|
DogStatsdAddr: "nope", |
|
DogStatsdTags: []string{"nope"}, |
|
}, |
|
Meta: map[string]string{ |
|
"key": "value1", |
|
}, |
|
} |
|
|
|
b := &Config{ |
|
Performance: Performance{ |
|
RaftMultiplier: 99, |
|
}, |
|
Bootstrap: true, |
|
BootstrapExpect: 3, |
|
Datacenter: "dc2", |
|
DataDir: "/tmp/bar", |
|
DNSRecursors: []string{"127.0.0.2:1001"}, |
|
DNSConfig: DNSConfig{ |
|
AllowStale: Bool(false), |
|
EnableTruncate: true, |
|
DisableCompression: true, |
|
MaxStale: 30 * time.Second, |
|
NodeTTL: 10 * time.Second, |
|
ServiceTTL: map[string]time.Duration{ |
|
"api": 10 * time.Second, |
|
}, |
|
UDPAnswerLimit: 4, |
|
RecursorTimeout: 30 * time.Second, |
|
}, |
|
Domain: "other", |
|
LogLevel: "info", |
|
NodeID: "bar", |
|
NodeName: "baz", |
|
ClientAddr: "127.0.0.2", |
|
BindAddr: "127.0.0.2", |
|
AdvertiseAddr: "127.0.0.2", |
|
AdvertiseAddrWan: "127.0.0.2", |
|
Ports: PortConfig{ |
|
DNS: 1, |
|
HTTP: 2, |
|
SerfLan: 4, |
|
SerfWan: 5, |
|
Server: 6, |
|
HTTPS: 7, |
|
}, |
|
Addresses: AddressConfig{ |
|
DNS: "127.0.0.1", |
|
HTTP: "127.0.0.2", |
|
HTTPS: "127.0.0.4", |
|
}, |
|
Server: true, |
|
LeaveOnTerm: Bool(true), |
|
SkipLeaveOnInt: Bool(true), |
|
RaftProtocol: 3, |
|
Autopilot: Autopilot{ |
|
CleanupDeadServers: Bool(true), |
|
}, |
|
EnableDebug: true, |
|
VerifyIncoming: true, |
|
VerifyOutgoing: true, |
|
CAFile: "test/ca.pem", |
|
CertFile: "test/cert.pem", |
|
KeyFile: "test/key.pem", |
|
TLSMinVersion: "tls12", |
|
Checks: []*CheckDefinition{nil}, |
|
Services: []*ServiceDefinition{nil}, |
|
StartJoin: []string{"1.1.1.1"}, |
|
StartJoinWan: []string{"1.1.1.1"}, |
|
EnableUi: true, |
|
UiDir: "/opt/consul-ui", |
|
EnableSyslog: true, |
|
RejoinAfterLeave: true, |
|
RetryJoin: []string{"1.1.1.1"}, |
|
RetryIntervalRaw: "10s", |
|
RetryInterval: 10 * time.Second, |
|
RetryJoinWan: []string{"1.1.1.1"}, |
|
RetryIntervalWanRaw: "10s", |
|
RetryIntervalWan: 10 * time.Second, |
|
ReconnectTimeoutLanRaw: "24h", |
|
ReconnectTimeoutLan: 24 * time.Hour, |
|
ReconnectTimeoutWanRaw: "36h", |
|
ReconnectTimeoutWan: 36 * time.Hour, |
|
CheckUpdateInterval: 8 * time.Minute, |
|
CheckUpdateIntervalRaw: "8m", |
|
ACLToken: "1111", |
|
ACLAgentMasterToken: "2222", |
|
ACLAgentToken: "3333", |
|
ACLMasterToken: "4444", |
|
ACLDatacenter: "dc2", |
|
ACLTTL: 15 * time.Second, |
|
ACLTTLRaw: "15s", |
|
ACLDownPolicy: "deny", |
|
ACLDefaultPolicy: "deny", |
|
ACLReplicationToken: "8765309", |
|
ACLEnforceVersion8: Bool(true), |
|
Watches: []map[string]interface{}{ |
|
map[string]interface{}{ |
|
"type": "keyprefix", |
|
"prefix": "foo/", |
|
"handler": "foobar", |
|
}, |
|
}, |
|
DisableRemoteExec: true, |
|
Telemetry: Telemetry{ |
|
StatsiteAddr: "127.0.0.1:7250", |
|
StatsitePrefix: "stats_prefix", |
|
StatsdAddr: "127.0.0.1:7251", |
|
DisableHostname: true, |
|
DogStatsdAddr: "127.0.0.1:7254", |
|
DogStatsdTags: []string{"tag_1:val_1", "tag_2:val_2"}, |
|
}, |
|
Meta: map[string]string{ |
|
"key": "value2", |
|
}, |
|
DisableUpdateCheck: true, |
|
DisableAnonymousSignature: true, |
|
HTTPAPIResponseHeaders: map[string]string{ |
|
"Access-Control-Allow-Origin": "*", |
|
}, |
|
UnixSockets: UnixSocketConfig{ |
|
UnixSocketPermissions{ |
|
Usr: "500", |
|
Grp: "500", |
|
Perms: "0700", |
|
}, |
|
}, |
|
AtlasInfrastructure: "hashicorp/prod", |
|
AtlasToken: "123456789", |
|
AtlasACLToken: "abcdefgh", |
|
AtlasJoin: true, |
|
RetryJoinEC2: RetryJoinEC2{ |
|
Region: "us-east-2", |
|
TagKey: "Key2", |
|
TagValue: "Value2", |
|
AccessKeyID: "foo", |
|
SecretAccessKey: "bar", |
|
}, |
|
SessionTTLMinRaw: "1000s", |
|
SessionTTLMin: 1000 * time.Second, |
|
AdvertiseAddrs: AdvertiseAddrsConfig{ |
|
SerfLan: &net.TCPAddr{}, |
|
SerfLanRaw: "127.0.0.5:1231", |
|
SerfWan: &net.TCPAddr{}, |
|
SerfWanRaw: "127.0.0.5:1232", |
|
RPC: &net.TCPAddr{}, |
|
RPCRaw: "127.0.0.5:1233", |
|
}, |
|
} |
|
|
|
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) |
|
} |
|
|
|
// An empty file shouldn't be read |
|
err = ioutil.WriteFile(filepath.Join(td, "d.json"), |
|
[]byte{}, 0664) |
|
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) |
|
} |
|
} |
|
|
|
func TestUnixSockets(t *testing.T) { |
|
path1, ok := unixSocketAddr("unix:///path/to/socket") |
|
if !ok || path1 != "/path/to/socket" { |
|
t.Fatalf("bad: %v %v", ok, path1) |
|
} |
|
|
|
path2, ok := unixSocketAddr("notunix://blah") |
|
if ok || path2 != "" { |
|
t.Fatalf("bad: %v %v", ok, path2) |
|
} |
|
}
|
|
|