groundwork (#140)

This commit is contained in:
Chris O'Haver 2019-04-08 16:21:40 -04:00 committed by GitHub
parent e6c1d12235
commit ac020ac1bc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 1082 additions and 0 deletions

View file

@ -0,0 +1,178 @@
package corefile
import (
"github.com/mholt/caddy"
"strings"
)
type Corefile struct {
Servers []*Server
}
type Server struct {
DomPorts []string
Plugins []*Plugin
}
type Plugin struct {
Name string
Args []string
Options []*Option
}
type Option struct {
Name string
Args []string
}
func New(s string) (Corefile, error) {
c := Corefile{}
cc := caddy.NewTestController("migration", s)
depth := 0
var cSvr *Server
var cPlg *Plugin
for cc.Next() {
if cc.Val() == "{" {
depth += 1
continue
} else if cc.Val() == "}" {
depth -= 1
continue
}
val := cc.Val()
args := cc.RemainingArgs()
switch depth {
case 0:
c.Servers = append(c.Servers,
&Server{
DomPorts: append([]string{val}, args...),
})
cSvr = c.Servers[len(c.Servers)-1]
case 1:
cSvr.Plugins = append(cSvr.Plugins,
&Plugin{
Name: val,
Args: args,
})
cPlg = cSvr.Plugins[len(cSvr.Plugins)-1]
case 2:
cPlg.Options = append(cPlg.Options,
&Option{
Name: val,
Args: args,
})
}
}
return c, nil
}
func (c *Corefile) ToString() (out string) {
strs := []string{}
for _, s := range c.Servers {
strs = append(strs, s.ToString())
}
return strings.Join(strs, "\n")
}
func (s *Server) ToString() (out string) {
str := strings.Join(s.DomPorts, " ")
strs := []string{}
for _, p := range s.Plugins {
strs = append(strs, strings.Repeat(" ", indent)+p.ToString())
}
if len(strs) > 0 {
str += " {\n" + strings.Join(strs, "\n") + "\n}\n"
}
return str
}
func (p *Plugin) ToString() (out string) {
str := strings.Join(append([]string{p.Name}, p.Args...), " ")
strs := []string{}
for _, o := range p.Options {
strs = append(strs, strings.Repeat(" ", indent*2)+o.ToString())
}
if len(strs) > 0 {
str += " {\n" + strings.Join(strs, "\n") + "\n" + strings.Repeat(" ", indent*1) + "}"
}
return str
}
func (o *Option) ToString() (out string) {
str := strings.Join(append([]string{o.Name}, o.Args...), " ")
return str
}
func (s *Server) FindMatch(def []*Server) (*Server, bool) {
NextServer:
for _, sDef := range def {
for i, dp := range sDef.DomPorts {
if dp == "*" {
continue
}
if dp == "***" {
return sDef, true
}
if i >= len(s.DomPorts) || dp != s.DomPorts[i] {
continue NextServer
}
}
if len(sDef.DomPorts) != len(s.DomPorts) {
continue
}
return sDef, true
}
return nil, false
}
func (p *Plugin) FindMatch(def []*Plugin) (*Plugin, bool) {
NextPlugin:
for _, pDef := range def {
if pDef.Name != p.Name {
continue
}
for i, arg := range pDef.Args {
if arg == "*" {
continue
}
if arg == "***" {
return pDef, true
}
if i >= len(p.Args) || arg != p.Args[i] {
continue NextPlugin
}
}
if len(pDef.Args) != len(p.Args) {
continue
}
return pDef, true
}
return nil, false
}
func (o *Option) FindMatch(def []*Option) (*Option, bool) {
NextPlugin:
for _, oDef := range def {
if oDef.Name != o.Name {
continue
}
for i, arg := range oDef.Args {
if arg == "*" {
continue
}
if arg == "***" {
return oDef, true
}
if i >= len(o.Args) || arg != o.Args[i] {
continue NextPlugin
}
}
if len(oDef.Args) != len(o.Args) {
continue
}
return oDef, true
}
return nil, false
}
const indent = 4

View file

@ -0,0 +1,121 @@
package corefile
import (
"testing"
)
func TestCorefile(t *testing.T) {
startCorefile := `.:53 {
error
health
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
upstream
fallthrough in-addr.arpa ip6.arpa
}
prometheus :9153
proxy . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}
.:5353 {
proxy . /etc/resolv.conf
}
`
c, err := New(startCorefile)
if err != nil {
t.Error(err)
}
got := c.ToString()
if got != startCorefile {
t.Errorf("Corefile did not match expected.\nExpected:\n%v\nGot:\n%v", startCorefile, got)
}
}
func TestServer_FindMatch(t *testing.T) {
tests := []struct{
server *Server
match bool
}{
{server: &Server{DomPorts: []string{".:53"}}, match: true},
{server: &Server{DomPorts: []string{".:54"}}, match: false},
{server: &Server{DomPorts: []string{"abc:53"}}, match: false},
{server: &Server{DomPorts: []string{"abc:53", "blah"}}, match: true},
{server: &Server{DomPorts: []string{"abc:53", "blah", "blah"}}, match: false},
{server: &Server{DomPorts: []string{"xyz:53"}}, match: true},
{server: &Server{DomPorts: []string{"xyz:53", "blah", "blah"}}, match: true},
}
def := []*Server{
{DomPorts: []string{".:53"}},
{DomPorts: []string{"abc:53", "*"}},
{DomPorts: []string{"xyz:53", "***"}},
}
for i, test := range tests {
_, match := test.server.FindMatch(def)
if match != test.match {
t.Errorf("In test #%v, expected match to be %v but got %v.", i, test.match, match)
}
}
}
func TestPlugin_FindMatch(t *testing.T) {
tests := []struct{
plugin *Plugin
match bool
}{
{plugin: &Plugin{Name: "plugin1", Args: []string{}}, match: true},
{plugin: &Plugin{Name: "plugin2", Args: []string{"1","1.5","2"}}, match: true},
{plugin: &Plugin{Name: "plugin3", Args: []string{"1","2","3","4"}}, match: true},
{plugin: &Plugin{Name: "plugin1", Args: []string{"a"}}, match: false},
{plugin: &Plugin{Name: "plugin2", Args: []string{"1","1.5","b"}}, match: false},
{plugin: &Plugin{Name: "plugin3", Args: []string{"a","2","3","4"}}, match: false},
{plugin: &Plugin{Name: "plugin4", Args: []string{}}, match: false},
}
def := []*Plugin{
{Name: "plugin1", Args: []string{}},
{Name: "plugin2", Args: []string{"1", "*", "2"}},
{Name: "plugin3", Args: []string{"1", "***"}},
}
for i, test := range tests {
_, match := test.plugin.FindMatch(def)
if match != test.match {
t.Errorf("In test #%v, expected match to be %v but got %v.", i, test.match, match)
}
}
}
func TestOption_FindMatch(t *testing.T) {
tests := []struct{
option *Plugin
match bool
}{
{option: &Plugin{Name: "option1", Args: []string{}}, match: true},
{option: &Plugin{Name: "option2", Args: []string{"1","1.5","2"}}, match: true},
{option: &Plugin{Name: "option3", Args: []string{"1","2","3","4"}}, match: true},
{option: &Plugin{Name: "option1", Args: []string{"a"}}, match: false},
{option: &Plugin{Name: "option2", Args: []string{"1","1.5","b"}}, match: false},
{option: &Plugin{Name: "option3", Args: []string{"a","2","3","4"}}, match: false},
{option: &Plugin{Name: "option4", Args: []string{}}, match: false},
}
def := []*Plugin{
{Name: "option1", Args: []string{}},
{Name: "option2", Args: []string{"1", "*", "2"}},
{Name: "option3", Args: []string{"1", "***"}},
}
for i, test := range tests {
_, match := test.option.FindMatch(def)
if match != test.match {
t.Errorf("In test #%v, expected match to be %v but got %v.", i, test.match, match)
}
}
}