mirror of
				https://github.com/coredns/coredns.git
				synced 2025-10-27 16:24:19 -04:00 
			
		
		
		
	* Removing unnecessary gitignore pattern
* Updating Makefile to run unittests for subpackages
* Adding Corefile validation to ignore overlapping zones
* Fixing SRV query handling
* Updating README.md now that SRV works
* Fixing debug message, adding code comment
* Clarifying implementation of zone normalization
* "Overlapping zones" is ill-defined. Reimplemented zone overlap/subzone
  checking to contain these functions in k8s middleware and provide
  better code comments explaining the normalization.
* Separate build verbosity from test verbosity
* Cleaning up comments to match repo code style
* Merging warning messages into single message
* Moving function docs to before function declaration
* Adding test cases for k8sclient connector
* Tests cover connector create and setting base url
* Fixed bugs in connector create and setting base url functions
* Updaing README to group and order development work
* Priority focused on achieving functional parity with SkyDNS.
* Adding work items to README and cleaning up formatting
* More README format cleaning
* List formating
* Refactoring k8s API call to allow dependency injection
* Add test cases for data parsing from k8s into dataobject structures
* URL is dependency-injected to allow replacement with a mock http
  server during test execution
* Adding more data validation for JSON parsing tests
* Adding test case for GetResourceList()
* Adding notes about SkyDNS embedded IP and port record names
* Marked test case implemented.
* Fixing formatting for example command.
* Fixing formatting
* Adding notes about Docker image building.
* Adding SkyDNS work item
* Updating TODO list
* Adding name template to Corefile to specify how k8s record names are assembled
* Adding template support for multi-segment zones
* Updating example CoreFile for k8s with template comment
* Misc whitespace cleanup
* Adding SkyDNS naming notes
* Adding namespace filtering to CoreFile config
* Updating example k8sCoreFile to specify namespaces
* Removing unused codepath
* Adding check for valid namespace
* More README TODO restructuring to focus effort
* Adding template validation while parsing CoreFile
* Record name template is considered invalid if it contains a symbol of the form ${bar} where the symbol
  "${bar}" is not an accepted template symbol.
* Refactoring generation of answer records
* Parse typeName out of query string
* Refactor answer record creation as operation over list of ServiceItems
* Moving k8s API caching into SkyDNS equivalency segment
* Adding function to assemble record names from template
* Warning: This commit may be broken. Syncing to get laptop code over to dev machine.
* More todo notes
* Adding comment describing sample test data.
* Update k8sCorefile
* Adding comment
* Adding filtering support for kubernetes "type"
* Required refactoring to support reuse of the StringInSlice function.
* Cleaning up formatting
* Adding note about SkyDNS supporting word "any".
* baseUrl -> baseURL
* Also removed debug statement from core/setup/kubernetes.go
* Fixing test breaking from Url -> URL naming changes
* Changing record name template language ${...} -> {...}
* Fix formatting with go fmt
* Updating all k8sclient data getters to return error value
* Adding error message to k8sclient data accessors
* Cleaning up setup for kubernetes
* Removed verbose nils in initial k8s middleware instance
* Set reasonable defaults if CoreFile has no parameters in the
kubernetes block. (k8s endpoint, and name template)
* Formatting cleanup -- go fmt
		
	
		
			
				
	
	
		
			167 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			167 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package nametemplate
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/miekg/coredns/middleware/kubernetes/util"
 | |
| )
 | |
| 
 | |
| // Likely symbols that require support:
 | |
| // {id}
 | |
| // {ip}
 | |
| // {portname}
 | |
| // {protocolname}
 | |
| // {servicename}
 | |
| // {namespace}
 | |
| // {type}              "svc" or "pod"
 | |
| // {zone}
 | |
| 
 | |
| // SkyDNS normal services have an A-record of the form "{servicename}.{namespace}.{type}.{zone}"
 | |
| // This resolves to the cluster IP of the service.
 | |
| 
 | |
| // SkyDNS headless services have an A-record of the form "{servicename}.{namespace}.{type}.{zone}"
 | |
| // This resolves to the set of IPs of the pods selected by the Service. Clients are expected to
 | |
| // consume the set or else use round-robin selection from the set.
 | |
| 
 | |
| var symbols = map[string]string{
 | |
| 	"service":   "{service}",
 | |
| 	"namespace": "{namespace}",
 | |
| 	"type":      "{type}",
 | |
| 	"zone":      "{zone}",
 | |
| }
 | |
| 
 | |
| var types = []string{
 | |
| 	"svc",
 | |
| 	"pod",
 | |
| }
 | |
| 
 | |
| // TODO: Validate that provided NameTemplate string only contains:
 | |
| //			* valid, known symbols, or
 | |
| //			* static strings
 | |
| 
 | |
| // TODO: Support collapsing multiple segments into a symbol. Either:
 | |
| //			* all left-over segments are used as the "service" name, or
 | |
| //			* some scheme like "{namespace}.{namespace}" means use
 | |
| //			  segments concatenated with a "." for the namespace, or
 | |
| //			* {namespace2:4} means use segements 2->4 for the namespace.
 | |
| 
 | |
| // TODO: possibly need to store length of segmented format to handle cases
 | |
| //       where query string segments to a shorter or longer list than the template.
 | |
| //		 When query string segments to shorter than template:
 | |
| //			* either wildcards are being used, or
 | |
| //			* we are not looking up an A, AAAA, or SRV record (eg NS), or
 | |
| //			* we can just short-circuit failure before hitting the k8s API.
 | |
| //		 Where the query string is longer than the template, need to define which
 | |
| //		 symbol consumes the other segments. Most likely this would be the servicename.
 | |
| //		 Also consider how to handle static strings in the format template.
 | |
| type NameTemplate struct {
 | |
| 	formatString string
 | |
| 	splitFormat  []string
 | |
| 	// Element is a map of element name :: index in the segmented record name for the named element
 | |
| 	Element map[string]int
 | |
| }
 | |
| 
 | |
| func (t *NameTemplate) SetTemplate(s string) error {
 | |
| 	var err error
 | |
| 	fmt.Println()
 | |
| 
 | |
| 	t.Element = map[string]int{}
 | |
| 
 | |
| 	t.formatString = s
 | |
| 	t.splitFormat = strings.Split(t.formatString, ".")
 | |
| 	for templateIndex, v := range t.splitFormat {
 | |
| 		elementPositionSet := false
 | |
| 		for name, symbol := range symbols {
 | |
| 			if v == symbol {
 | |
| 				t.Element[name] = templateIndex
 | |
| 				elementPositionSet = true
 | |
| 				break
 | |
| 			}
 | |
| 		}
 | |
| 		if !elementPositionSet {
 | |
| 			if strings.Contains(v, "{") {
 | |
| 				err = errors.New("Record name template contains the unknown symbol '" + v + "'")
 | |
| 				fmt.Printf("[debug] %v\n", err)
 | |
| 				return err
 | |
| 			} else {
 | |
| 				fmt.Printf("[debug] Template string has static element '%v'\n", v)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| // TODO: Find a better way to pull the data segments out of the
 | |
| //       query string based on the template. Perhaps it is better
 | |
| //		 to treat the query string segments as a reverse stack and
 | |
| //       step down the stack to find the right element.
 | |
| 
 | |
| func (t *NameTemplate) GetZoneFromSegmentArray(segments []string) string {
 | |
| 	if index, ok := t.Element["zone"]; !ok {
 | |
| 		return ""
 | |
| 	} else {
 | |
| 		return strings.Join(segments[index:len(segments)], ".")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (t *NameTemplate) GetNamespaceFromSegmentArray(segments []string) string {
 | |
| 	return t.GetSymbolFromSegmentArray("namespace", segments)
 | |
| }
 | |
| 
 | |
| func (t *NameTemplate) GetServiceFromSegmentArray(segments []string) string {
 | |
| 	return t.GetSymbolFromSegmentArray("service", segments)
 | |
| }
 | |
| 
 | |
| func (t *NameTemplate) GetTypeFromSegmentArray(segments []string) string {
 | |
| 	typeSegment := t.GetSymbolFromSegmentArray("type", segments)
 | |
| 
 | |
| 	// Limit type to known types symbols
 | |
| 	if util.StringInSlice(typeSegment, types) {
 | |
| 		return ""
 | |
| 	}
 | |
| 
 | |
| 	return typeSegment
 | |
| }
 | |
| 
 | |
| func (t *NameTemplate) GetSymbolFromSegmentArray(symbol string, segments []string) string {
 | |
| 	if index, ok := t.Element[symbol]; !ok {
 | |
| 		return ""
 | |
| 	} else {
 | |
| 		return segments[index]
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // GetRecordNameFromNameValues returns the string produced by applying the
 | |
| // values to the NameTemplate format string.
 | |
| func (t *NameTemplate) GetRecordNameFromNameValues(values NameValues) string {
 | |
| 	recordName := make([]string, len(t.splitFormat))
 | |
| 	copy(recordName[:], t.splitFormat)
 | |
| 
 | |
| 	for name, index := range t.Element {
 | |
| 		if index == -1 {
 | |
| 			continue
 | |
| 		}
 | |
| 		switch name {
 | |
| 		case "type":
 | |
| 			recordName[index] = values.TypeName
 | |
| 		case "service":
 | |
| 			recordName[index] = values.ServiceName
 | |
| 		case "namespace":
 | |
| 			recordName[index] = values.Namespace
 | |
| 		case "zone":
 | |
| 			recordName[index] = values.Zone
 | |
| 		}
 | |
| 	}
 | |
| 	return strings.Join(recordName, ".")
 | |
| }
 | |
| 
 | |
| type NameValues struct {
 | |
| 	ServiceName string
 | |
| 	Namespace   string
 | |
| 	TypeName    string
 | |
| 	Zone        string
 | |
| }
 |