mirror of
				https://github.com/coredns/coredns.git
				synced 2025-10-31 10:13:14 -04:00 
			
		
		
		
	plugin/view: Advanced routing interface and new 'view' plugin (#5538)
* 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>
This commit is contained in:
		
							
								
								
									
										47
									
								
								plugin/pkg/expression/expression.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								plugin/pkg/expression/expression.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| package expression | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	"net" | ||||
|  | ||||
| 	"github.com/coredns/coredns/plugin/metadata" | ||||
| 	"github.com/coredns/coredns/request" | ||||
| ) | ||||
|  | ||||
| // DefaultEnv returns the default set of custom state variables and functions available to for use in expression evaluation. | ||||
| func DefaultEnv(ctx context.Context, state *request.Request) map[string]interface{} { | ||||
| 	return map[string]interface{}{ | ||||
| 		"incidr": func(ipStr, cidrStr string) (bool, error) { | ||||
| 			ip := net.ParseIP(ipStr) | ||||
| 			if ip == nil { | ||||
| 				return false, errors.New("first argument is not an IP address") | ||||
| 			} | ||||
| 			_, cidr, err := net.ParseCIDR(cidrStr) | ||||
| 			if err != nil { | ||||
| 				return false, err | ||||
| 			} | ||||
| 			return cidr.Contains(ip), nil | ||||
| 		}, | ||||
| 		"metadata": func(label string) string { | ||||
| 			f := metadata.ValueFunc(ctx, label) | ||||
| 			if f == nil { | ||||
| 				return "" | ||||
| 			} | ||||
| 			return f() | ||||
| 		}, | ||||
| 		"type":        state.Type, | ||||
| 		"name":        state.Name, | ||||
| 		"class":       state.Class, | ||||
| 		"proto":       state.Proto, | ||||
| 		"size":        state.Len, | ||||
| 		"client_ip":   state.IP, | ||||
| 		"port":        state.Port, | ||||
| 		"id":          func() int { return int(state.Req.Id) }, | ||||
| 		"opcode":      func() int { return state.Req.Opcode }, | ||||
| 		"do":          state.Do, | ||||
| 		"bufsize":     state.Size, | ||||
| 		"server_ip":   state.LocalIP, | ||||
| 		"server_port": state.LocalPort, | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										73
									
								
								plugin/pkg/expression/expression_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								plugin/pkg/expression/expression_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| package expression | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/coredns/coredns/plugin/metadata" | ||||
| 	"github.com/coredns/coredns/request" | ||||
| ) | ||||
|  | ||||
| func TestInCidr(t *testing.T) { | ||||
| 	incidr := DefaultEnv(context.Background(), &request.Request{})["incidr"] | ||||
|  | ||||
| 	cases := []struct { | ||||
| 		ip        string | ||||
| 		cidr      string | ||||
| 		expected  bool | ||||
| 		shouldErr bool | ||||
| 	}{ | ||||
| 		// positive | ||||
| 		{ip: "1.2.3.4", cidr: "1.2.0.0/16", expected: true, shouldErr: false}, | ||||
| 		{ip: "10.2.3.4", cidr: "1.2.0.0/16", expected: false, shouldErr: false}, | ||||
| 		{ip: "1:2::3:4", cidr: "1:2::/64", expected: true, shouldErr: false}, | ||||
| 		{ip: "A:2::3:4", cidr: "1:2::/64", expected: false, shouldErr: false}, | ||||
| 		// negative | ||||
| 		{ip: "1.2.3.4", cidr: "invalid", shouldErr: true}, | ||||
| 		{ip: "invalid", cidr: "1.2.0.0/16", shouldErr: true}, | ||||
| 	} | ||||
|  | ||||
| 	for i, c := range cases { | ||||
| 		r, err := incidr.(func(string, string) (bool, error))(c.ip, c.cidr) | ||||
| 		if err != nil && !c.shouldErr { | ||||
| 			t.Errorf("Test %d: unexpected error %v", i, err) | ||||
| 			continue | ||||
| 		} | ||||
| 		if err == nil && c.shouldErr { | ||||
| 			t.Errorf("Test %d: expected error", i) | ||||
| 			continue | ||||
| 		} | ||||
| 		if c.shouldErr { | ||||
| 			continue | ||||
| 		} | ||||
| 		if r != c.expected { | ||||
| 			t.Errorf("Test %d: expected %v", i, c.expected) | ||||
| 			continue | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestMetadata(t *testing.T) { | ||||
| 	ctx := metadata.ContextWithMetadata(context.Background()) | ||||
| 	metadata.SetValueFunc(ctx, "test/metadata", func() string { | ||||
| 		return "success" | ||||
| 	}) | ||||
| 	f := DefaultEnv(ctx, &request.Request{})["metadata"] | ||||
|  | ||||
| 	cases := []struct { | ||||
| 		label     string | ||||
| 		expected  string | ||||
| 		shouldErr bool | ||||
| 	}{ | ||||
| 		{label: "test/metadata", expected: "success"}, | ||||
| 		{label: "test/nonexistent", expected: ""}, | ||||
| 	} | ||||
|  | ||||
| 	for i, c := range cases { | ||||
| 		r := f.(func(string) string)(c.label) | ||||
| 		if r != c.expected { | ||||
| 			t.Errorf("Test %d: expected %v", i, c.expected) | ||||
| 			continue | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user