aboutsummaryrefslogtreecommitdiff
path: root/middleware/kubernetes/nametemplate/nametemplate.go
diff options
context:
space:
mode:
Diffstat (limited to 'middleware/kubernetes/nametemplate/nametemplate.go')
-rw-r--r--middleware/kubernetes/nametemplate/nametemplate.go166
1 files changed, 166 insertions, 0 deletions
diff --git a/middleware/kubernetes/nametemplate/nametemplate.go b/middleware/kubernetes/nametemplate/nametemplate.go
new file mode 100644
index 000000000..e61c036e8
--- /dev/null
+++ b/middleware/kubernetes/nametemplate/nametemplate.go
@@ -0,0 +1,166 @@
+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
+}