Merge pull request #224 from hashicorp/f-case-insensitivity
DNS case-insensitivity
This commit is contained in:
commit
7fa2197948
|
@ -1,3 +1,9 @@
|
|||
## 0.3.2 (Unreleased)
|
||||
|
||||
IMPROVEMENTS:
|
||||
|
||||
* DNS case-insensitivity [GH-189]
|
||||
|
||||
## 0.3.1 (July 21, 2014)
|
||||
|
||||
FEATURES:
|
||||
|
|
|
@ -248,7 +248,7 @@ func (d *DNSServer) dispatch(network string, req, resp *dns.Msg) {
|
|||
datacenter := d.agent.config.Datacenter
|
||||
|
||||
// Get the QName without the domain suffix
|
||||
qName := dns.Fqdn(req.Question[0].Name)
|
||||
qName := strings.ToLower(dns.Fqdn(req.Question[0].Name))
|
||||
qName = strings.TrimSuffix(qName, d.domain)
|
||||
|
||||
// Split into the label parts
|
||||
|
|
|
@ -136,6 +136,40 @@ func TestDNS_NodeLookup(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestDNS_CaseInsensitiveNodeLookup(t *testing.T) {
|
||||
dir, srv := makeDNSServer(t)
|
||||
defer os.RemoveAll(dir)
|
||||
defer srv.agent.Shutdown()
|
||||
|
||||
testutil.WaitForLeader(t, srv.agent.RPC, "dc1")
|
||||
|
||||
// Register node
|
||||
args := &structs.RegisterRequest{
|
||||
Datacenter: "dc1",
|
||||
Node: "Foo",
|
||||
Address: "127.0.0.1",
|
||||
}
|
||||
|
||||
var out struct{}
|
||||
if err := srv.agent.RPC("Catalog.Register", args, &out); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion("fOO.node.dc1.consul.", dns.TypeANY)
|
||||
|
||||
c := new(dns.Client)
|
||||
addr, _ := srv.agent.config.ClientListener(srv.agent.config.Ports.DNS)
|
||||
in, _, err := c.Exchange(m, addr.String())
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if len(in.Answer) != 1 {
|
||||
t.Fatalf("empty lookup: %#v", in)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDNS_NodeLookup_PeriodName(t *testing.T) {
|
||||
dir, srv := makeDNSServer(t)
|
||||
defer os.RemoveAll(dir)
|
||||
|
@ -336,6 +370,45 @@ func TestDNS_ServiceLookup(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestDNS_CaseInsensitiveServiceLookup(t *testing.T) {
|
||||
dir, srv := makeDNSServer(t)
|
||||
defer os.RemoveAll(dir)
|
||||
defer srv.agent.Shutdown()
|
||||
|
||||
testutil.WaitForLeader(t, srv.agent.RPC, "dc1")
|
||||
|
||||
// Register node
|
||||
args := &structs.RegisterRequest{
|
||||
Datacenter: "dc1",
|
||||
Node: "foo",
|
||||
Address: "127.0.0.1",
|
||||
Service: &structs.NodeService{
|
||||
Service: "Db",
|
||||
Tags: []string{"Master"},
|
||||
Port: 12345,
|
||||
},
|
||||
}
|
||||
|
||||
var out struct{}
|
||||
if err := srv.agent.RPC("Catalog.Register", args, &out); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion("mASTER.dB.service.consul.", dns.TypeSRV)
|
||||
|
||||
c := new(dns.Client)
|
||||
addr, _ := srv.agent.config.ClientListener(srv.agent.config.Ports.DNS)
|
||||
in, _, err := c.Exchange(m, addr.String())
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if len(in.Answer) != 1 {
|
||||
t.Fatalf("empty lookup: %#v", in)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDNS_ServiceLookup_TagPeriod(t *testing.T) {
|
||||
dir, srv := makeDNSServer(t)
|
||||
defer os.RemoveAll(dir)
|
||||
|
|
|
@ -220,13 +220,13 @@ func TestCatalogListNodes(t *testing.T) {
|
|||
})
|
||||
|
||||
// Server node is auto added from Serf
|
||||
if out.Nodes[0].Node != s1.config.NodeName {
|
||||
if out.Nodes[1].Node != s1.config.NodeName {
|
||||
t.Fatalf("bad: %v", out)
|
||||
}
|
||||
if out.Nodes[1].Node != "foo" {
|
||||
if out.Nodes[0].Node != "foo" {
|
||||
t.Fatalf("bad: %v", out)
|
||||
}
|
||||
if out.Nodes[1].Address != "127.0.0.1" {
|
||||
if out.Nodes[0].Address != "127.0.0.1" {
|
||||
t.Fatalf("bad: %v", out)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,12 +45,13 @@ type MDBTables []*MDBTable
|
|||
// An Index is named, and uses a series of column values to
|
||||
// map to the row-id containing the table
|
||||
type MDBIndex struct {
|
||||
AllowBlank bool // Can fields be blank
|
||||
Unique bool // Controls if values are unique
|
||||
Fields []string // Fields are used to build the index
|
||||
IdxFunc IndexFunc // Can be used to provide custom indexing
|
||||
Virtual bool // Virtual index does not exist, but can be used for queries
|
||||
RealIndex string // Virtual indexes use a RealIndex for iteration
|
||||
AllowBlank bool // Can fields be blank
|
||||
Unique bool // Controls if values are unique
|
||||
Fields []string // Fields are used to build the index
|
||||
IdxFunc IndexFunc // Can be used to provide custom indexing
|
||||
Virtual bool // Virtual index does not exist, but can be used for queries
|
||||
RealIndex string // Virtual indexes use a RealIndex for iteration
|
||||
CaseInsensitive bool // Controls if values are case-insensitive
|
||||
|
||||
table *MDBTable
|
||||
name string
|
||||
|
@ -426,6 +427,10 @@ func (t *MDBTable) getIndex(index string, parts []string) (*MDBIndex, []byte, er
|
|||
return nil, nil, tooManyFields
|
||||
}
|
||||
|
||||
if idx.CaseInsensitive {
|
||||
parts = ToLowerList(parts)
|
||||
}
|
||||
|
||||
// Construct the key
|
||||
key := idx.keyFromParts(parts...)
|
||||
return idx, key, nil
|
||||
|
@ -613,6 +618,9 @@ func (i *MDBIndex) keyFromObject(obj interface{}) ([]byte, error) {
|
|||
if !i.AllowBlank && val == "" {
|
||||
return nil, fmt.Errorf("Field '%s' must be set: %#v", field, obj)
|
||||
}
|
||||
if i.CaseInsensitive {
|
||||
val = strings.ToLower(val)
|
||||
}
|
||||
parts = append(parts, val)
|
||||
}
|
||||
key := i.keyFromParts(parts...)
|
||||
|
|
|
@ -179,6 +179,7 @@ func (s *StateStore) initialize() error {
|
|||
"id": &MDBIndex{
|
||||
Unique: true,
|
||||
Fields: []string{"Node"},
|
||||
CaseInsensitive: true,
|
||||
},
|
||||
},
|
||||
Decoder: func(buf []byte) interface{} {
|
||||
|
@ -200,6 +201,7 @@ func (s *StateStore) initialize() error {
|
|||
"service": &MDBIndex{
|
||||
AllowBlank: true,
|
||||
Fields: []string{"ServiceName"},
|
||||
CaseInsensitive: true,
|
||||
},
|
||||
},
|
||||
Decoder: func(buf []byte) interface{} {
|
||||
|
@ -640,7 +642,7 @@ func serviceTagFilter(l []interface{}, tag string) []interface{} {
|
|||
n := len(l)
|
||||
for i := 0; i < n; i++ {
|
||||
srv := l[i].(*structs.ServiceNode)
|
||||
if !strContains(srv.ServiceTags, tag) {
|
||||
if !strContains(ToLowerList(srv.ServiceTags), strings.ToLower(tag)) {
|
||||
l[i], l[n-1] = l[n-1], nil
|
||||
i--
|
||||
n--
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/serf/serf"
|
||||
)
|
||||
|
@ -68,6 +69,14 @@ func strContains(l []string, s string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func ToLowerList(l []string) []string {
|
||||
var out []string
|
||||
for _, value := range l {
|
||||
out = append(out, strings.ToLower(value))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// ensurePath is used to make sure a path exists
|
||||
func ensurePath(path string, dir bool) error {
|
||||
if !dir {
|
||||
|
|
|
@ -18,6 +18,15 @@ func TestStrContains(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestToLowerList(t *testing.T) {
|
||||
l := []string{"ABC", "Abc", "abc"}
|
||||
for _, value := range ToLowerList(l) {
|
||||
if value != "abc" {
|
||||
t.Fatalf("failed lowercasing")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsPrivateIP(t *testing.T) {
|
||||
if !isPrivateIP("192.168.1.1") {
|
||||
t.Fatalf("bad")
|
||||
|
|
|
@ -20,7 +20,8 @@ with no failing health checks. It's that simple!
|
|||
There are a number of [configuration options](/docs/agent/options.html) that
|
||||
are important for the DNS interface. They are `client_addr`, `ports.dns`, `recursor`,
|
||||
`domain`, and `dns_config`. By default Consul will listen on 127.0.0.1:8600 for DNS queries
|
||||
in the "consul." domain, without support for DNS recursion.
|
||||
in the "consul." domain, without support for DNS recursion. All queries are case-insensitive, a
|
||||
name lookup for `PostgreSQL.node.dc1.consul` will find all nodes named `postgresql`, no matter of case.
|
||||
|
||||
There are a few ways to use the DNS interface. One option is to use a custom
|
||||
DNS resolver library and point it at Consul. Another option is to set Consul
|
||||
|
|
Loading…
Reference in a new issue