mirror of
				https://github.com/coredns/coredns.git
				synced 2025-11-03 02:33:21 -05:00 
			
		
		
		
	* introduce new interface "dnsserver.Viewer", that allows a plugin implementing it to decide if a query should be routed into its server block. * add new plugin "view", that uses the new interface to enable a user to define expression based conditions that must be met for a query to be routed to its server block. Signed-off-by: Chris O'Haver <cohaver@infoblox.com>
		
			
				
	
	
		
			164 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			164 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package test
 | 
						|
 | 
						|
import (
 | 
						|
	"strings"
 | 
						|
	"testing"
 | 
						|
 | 
						|
	"github.com/coredns/coredns/plugin/test"
 | 
						|
 | 
						|
	"github.com/miekg/dns"
 | 
						|
)
 | 
						|
 | 
						|
func TestView(t *testing.T) {
 | 
						|
	// Hack to get an available port - We spin up a temporary dummy coredns on :0 to get the port number, then we re-use
 | 
						|
	// that one port consistently across all server blocks.
 | 
						|
	corefile := `example.org:0 {
 | 
						|
		erratic
 | 
						|
	}`
 | 
						|
	tmp, addr, _, err := CoreDNSServerAndPorts(corefile)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("Could not get CoreDNS serving instance: %s", err)
 | 
						|
	}
 | 
						|
 | 
						|
	port := addr[strings.LastIndex(addr, ":")+1:]
 | 
						|
 | 
						|
	// Corefile with test views
 | 
						|
	corefile = `
 | 
						|
      # split-type config: splits quries for A/AAAA into separate views
 | 
						|
      split-type:` + port + ` {
 | 
						|
		view test-view-a {
 | 
						|
          expr type() == 'A'
 | 
						|
	    }
 | 
						|
        hosts {
 | 
						|
          1.2.3.4 test.split-type
 | 
						|
        }
 | 
						|
      }
 | 
						|
      split-type:` + port + ` {
 | 
						|
		view test-view-aaaa {
 | 
						|
          expr type() == 'AAAA'
 | 
						|
	    }
 | 
						|
        hosts {
 | 
						|
          1:2:3::4 test.split-type
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      # split-name config: splits queries into separate views based on first label in query name ("one", "two")
 | 
						|
      split-name:` + port + ` {
 | 
						|
		view test-view-1 {
 | 
						|
          expr name() matches '^one\\..*\\.split-name\\.$'
 | 
						|
	    }
 | 
						|
        hosts {
 | 
						|
          1.1.1.1 one.test.split-name one.test.test.test.split-name
 | 
						|
        }
 | 
						|
      }
 | 
						|
      split-name:` + port + ` {
 | 
						|
		view test-view-2 {
 | 
						|
          expr name() matches '^two\\..*\\.split-name\\.$'
 | 
						|
	    }
 | 
						|
        hosts {
 | 
						|
          2.2.2.2 two.test.split-name two.test.test.test.split-name
 | 
						|
        }
 | 
						|
      }
 | 
						|
      split-name:` + port + ` {
 | 
						|
        hosts {
 | 
						|
          3.3.3.3 default.test.split-name
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
     # metadata config: verifies that metadata is properly collected by the server,
 | 
						|
     # and that metadata function correctly looks up the value of the metadata.
 | 
						|
     metadata:` + port + ` {
 | 
						|
       metadata
 | 
						|
       view test-view-meta1 {
 | 
						|
         # This is never true
 | 
						|
         expr metadata('view/name') == 'not-the-view-name'
 | 
						|
	   }
 | 
						|
       hosts {
 | 
						|
         1.1.1.1 test.metadata
 | 
						|
       }
 | 
						|
     }
 | 
						|
     metadata:` + port + ` {
 | 
						|
       view test-view-meta2 {
 | 
						|
         # This is never true. The metadata plugin is not enabled in this server block so the metadata function returns
 | 
						|
         # an empty string
 | 
						|
         expr metadata('view/name') == 'test-view-meta2'
 | 
						|
	   }
 | 
						|
       hosts {
 | 
						|
         2.2.2.2 test.metadata
 | 
						|
       }
 | 
						|
     }
 | 
						|
     metadata:` + port + ` {
 | 
						|
       metadata
 | 
						|
       view test-view-meta3 {
 | 
						|
         # This is always true.  Queries in the zone 'metadata.' should always be served using this view.
 | 
						|
         expr metadata('view/name') == 'test-view-meta3'
 | 
						|
	   }
 | 
						|
       hosts {
 | 
						|
         2.2.2.2 test.metadata
 | 
						|
       }
 | 
						|
     }
 | 
						|
     metadata:` + port + ` {
 | 
						|
       # This block should never be reached since the prior view in the same zone is always true
 | 
						|
       hosts {
 | 
						|
         3.3.3.3 test.metadata
 | 
						|
       }
 | 
						|
     }
 | 
						|
    `
 | 
						|
 | 
						|
	i, addr, _, err := CoreDNSServerAndPorts(corefile)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("Could not get CoreDNS serving instance: %s", err)
 | 
						|
	}
 | 
						|
	// there are multiple sever blocks, but they are all on the same port, so it's a single server instance to stop
 | 
						|
	defer i.Stop()
 | 
						|
	// stop the temporary instance before starting tests.
 | 
						|
	tmp.Stop()
 | 
						|
 | 
						|
	viewTest(t, "split-type A", addr, "test.split-type.", dns.TypeA, dns.RcodeSuccess,
 | 
						|
		[]dns.RR{test.A("test.split-type.	303	IN	A	1.2.3.4")})
 | 
						|
 | 
						|
	viewTest(t, "split-type AAAA", addr, "test.split-type.", dns.TypeAAAA, dns.RcodeSuccess,
 | 
						|
		[]dns.RR{test.AAAA("test.split-type.	303	IN	AAAA	1:2:3::4")})
 | 
						|
 | 
						|
	viewTest(t, "split-name one.test.test.test.split-name", addr, "one.test.test.test.split-name.", dns.TypeA, dns.RcodeSuccess,
 | 
						|
		[]dns.RR{test.A("one.test.test.test.split-name.	303	IN	A	1.1.1.1")})
 | 
						|
 | 
						|
	viewTest(t, "split-name one.test.split-name", addr, "one.test.split-name.", dns.TypeA, dns.RcodeSuccess,
 | 
						|
		[]dns.RR{test.A("one.test.split-name.	303	IN	A	1.1.1.1")})
 | 
						|
 | 
						|
	viewTest(t, "split-name two.test.test.test.split-name", addr, "two.test.test.test.split-name.", dns.TypeA, dns.RcodeSuccess,
 | 
						|
		[]dns.RR{test.A("two.test.test.test.split-name.	303	IN	A	2.2.2.2")})
 | 
						|
 | 
						|
	viewTest(t, "split-name two.test.split-name", addr, "two.test.split-name.", dns.TypeA, dns.RcodeSuccess,
 | 
						|
		[]dns.RR{test.A("two.test.split-name.	303	IN	A	2.2.2.2")})
 | 
						|
 | 
						|
	viewTest(t, "split-name default.test.split-name", addr, "default.test.split-name.", dns.TypeA, dns.RcodeSuccess,
 | 
						|
		[]dns.RR{test.A("default.test.split-name.	303	IN	A	3.3.3.3")})
 | 
						|
 | 
						|
	viewTest(t, "metadata test.metadata", addr, "test.metadata.", dns.TypeA, dns.RcodeSuccess,
 | 
						|
		[]dns.RR{test.A("test.metadata.	303	IN	A	2.2.2.2")})
 | 
						|
}
 | 
						|
 | 
						|
func viewTest(t *testing.T, testName, addr, qname string, qtype uint16, expectRcode int, expectAnswers []dns.RR) {
 | 
						|
	t.Run(testName, func(t *testing.T) {
 | 
						|
		m := new(dns.Msg)
 | 
						|
 | 
						|
		m.SetQuestion(qname, qtype)
 | 
						|
		resp, err := dns.Exchange(m, addr)
 | 
						|
		if err != nil {
 | 
						|
			t.Fatalf("Expected to receive reply, but didn't: %s", err)
 | 
						|
		}
 | 
						|
 | 
						|
		tc := test.Case{
 | 
						|
			Qname: qname, Qtype: qtype,
 | 
						|
			Rcode:  expectRcode,
 | 
						|
			Answer: expectAnswers,
 | 
						|
		}
 | 
						|
 | 
						|
		err = test.SortAndCheck(resp, tc)
 | 
						|
		if err != nil {
 | 
						|
			t.Error(err)
 | 
						|
		}
 | 
						|
	})
 | 
						|
}
 |