From d7f87ddf2a9942e9ee4c3a94430d377ed2eec2ce Mon Sep 17 00:00:00 2001 From: Chris O'Haver Date: Mon, 13 May 2019 16:23:53 -0400 Subject: [PATCH] k8s migration: add handler for multi proxy to forward (#164) add global postprocess and handler for multi proxy to forward --- kubernetes/migration/corefile/corefile.go | 4 +- kubernetes/migration/migrate.go | 12 +++- kubernetes/migration/migrate_test.go | 56 +++++++++++++++++- kubernetes/migration/versions.go | 72 +++++++++++++++++++---- 4 files changed, 129 insertions(+), 15 deletions(-) diff --git a/kubernetes/migration/corefile/corefile.go b/kubernetes/migration/corefile/corefile.go index 2695588..511df87 100644 --- a/kubernetes/migration/corefile/corefile.go +++ b/kubernetes/migration/corefile/corefile.go @@ -25,7 +25,7 @@ type Option struct { Args []string } -func New(s string) (Corefile, error) { +func New(s string) (*Corefile, error) { c := Corefile{} cc := caddy.NewTestController("migration", s) depth := 0 @@ -63,7 +63,7 @@ func New(s string) (Corefile, error) { }) } } - return c, nil + return &c, nil } func (c *Corefile) ToString() (out string) { diff --git a/kubernetes/migration/migrate.go b/kubernetes/migration/migrate.go index 9f6d415..54ca6b0 100644 --- a/kubernetes/migration/migrate.go +++ b/kubernetes/migration/migrate.go @@ -200,7 +200,17 @@ func Migrate(fromCoreDNSVersion, toCoreDNSVersion, corefileStr string, deprecati newSrvs = append(newSrvs, newSrv) } - cf = corefile.Corefile{Servers: newSrvs} + + cf = &corefile.Corefile{Servers: newSrvs} + + // apply any global corefile level post processing + if Versions[v].postProcess != nil { + cf, err = Versions[v].postProcess(cf) + if err != nil { + return "", err + } + } + if v == toCoreDNSVersion { break } diff --git a/kubernetes/migration/migrate_test.go b/kubernetes/migration/migrate_test.go index 93a44ca..3ef1bef 100644 --- a/kubernetes/migration/migrate_test.go +++ b/kubernetes/migration/migrate_test.go @@ -1,6 +1,7 @@ package migration import ( + "github.com/andreyvit/diff" "testing" ) @@ -130,6 +131,59 @@ func TestMigrate(t *testing.T) { loop ready } +`, + }, + { + name: "handle multiple proxy plugins", + fromVersion: "1.1.3", + toVersion: "1.5.0", + deprecations: true, + startCorefile: `.:53 { + errors + health + kubernetes cluster.local in-addr.arpa ip6.arpa { + pods insecure + upstream + fallthrough in-addr.arpa ip6.arpa + } + prometheus :9153 + proxy mystub-1.example.org 1.2.3.4 + proxy mystub-2.example.org 5.6.7.8 + proxy . /etc/resolv.conf + cache 30 + reload + loadbalance +} +`, + expectedCorefile: `.:53 { + errors + health + kubernetes cluster.local in-addr.arpa ip6.arpa { + pods insecure + fallthrough in-addr.arpa ip6.arpa + } + prometheus :9153 + forward . /etc/resolv.conf + cache 30 + reload + loadbalance + loop + ready +} + +mystub-1.example.org { + forward . 1.2.3.4 + loop + errors + cache 30 +} + +mystub-2.example.org { + forward . 5.6.7.8 + loop + errors + cache 30 +} `, }, } @@ -144,7 +198,7 @@ func TestMigrate(t *testing.T) { } if result != testCase.expectedCorefile { - t.Errorf("expected %v; got %v", testCase.expectedCorefile, result) + t.Errorf("expected -> got diffs:\n%v", diff.LineDiff(testCase.expectedCorefile, result)) } }) } diff --git a/kubernetes/migration/versions.go b/kubernetes/migration/versions.go index 3a3e32e..7b73209 100644 --- a/kubernetes/migration/versions.go +++ b/kubernetes/migration/versions.go @@ -1,33 +1,40 @@ package migration import ( + "errors" "github.com/coredns/deployment/kubernetes/migration/corefile" ) type plugin struct { status string - add serverActionFn replacedBy string additional string - action pluginActionFn options map[string]option + action pluginActionFn // action affecting this plugin only + add serverActionFn // action to add a new plugin to the server block } type option struct { status string - add pluginActionFn replacedBy string additional string - action optionActionFn + action optionActionFn // take action affecting this option only + add pluginActionFn // take action to add the option to the plugin } type release struct { k8sRelease string nextVersion string dockerImageSHA string - plugins map[string]plugin + plugins map[string]plugin // list of plugins with deprecation status and migration actions - // defaultConf hold the default Corefile template packaged with the corresponding k8sRelease. + // postProcess is a post processing action to take on the corefile as a whole. Used for complex migration + // tasks that dont fit well into the modular plugin/option migration framework. For example, when the + // action on a plugin would need to extend beyond the scope of that plugin (affecting other plugins, or + // server blocks, etc). e.g. Splitting plugins out into separate server blocks. + postProcess corefileAction + + // defaultConf holds the default Corefile template packaged with the corresponding k8sRelease. // Wildcards are used for fuzzy matching: // "*" matches exactly one token // "***" matches 0 all remaining tokens on the line @@ -36,7 +43,8 @@ type release struct { defaultConf string } -type serverActionFn func(server *corefile.Server) (*corefile.Server, error) +type corefileAction func(*corefile.Corefile) (*corefile.Corefile, error) +type serverActionFn func(*corefile.Server) (*corefile.Server, error) type pluginActionFn func(*corefile.Plugin) (*corefile.Plugin, error) type optionActionFn func(*corefile.Option) (*corefile.Option, error) @@ -48,7 +56,7 @@ func renamePlugin(p *corefile.Plugin, to string) (*corefile.Plugin, error) { return p, nil } -func addToServerBlocksWithPlugins(sb *corefile.Server, newPlugin *corefile.Plugin, with []string) (*corefile.Server, error) { +func addToServerBlockWithPlugins(sb *corefile.Server, newPlugin *corefile.Plugin, with []string) (*corefile.Server, error) { if len(with) == 0 { // add to all blocks sb.Plugins = append(sb.Plugins, newPlugin) @@ -67,15 +75,15 @@ func addToServerBlocksWithPlugins(sb *corefile.Server, newPlugin *corefile.Plugi } func addToKubernetesServerBlocks(sb *corefile.Server, newPlugin *corefile.Plugin) (*corefile.Server, error) { - return addToServerBlocksWithPlugins(sb, newPlugin, []string{"kubernetes"}) + return addToServerBlockWithPlugins(sb, newPlugin, []string{"kubernetes"}) } func addToForwardingServerBlocks(sb *corefile.Server, newPlugin *corefile.Plugin) (*corefile.Server, error) { - return addToServerBlocksWithPlugins(sb, newPlugin, []string{"forward", "proxy"}) + return addToServerBlockWithPlugins(sb, newPlugin, []string{"forward", "proxy"}) } func addToAllServerBlocks(sb *corefile.Server, newPlugin *corefile.Plugin) (*corefile.Server, error) { - return addToServerBlocksWithPlugins(sb, newPlugin, []string{}) + return addToServerBlockWithPlugins(sb, newPlugin, []string{}) } var Versions = map[string]release{ @@ -164,6 +172,7 @@ var Versions = map[string]release{ "reload": {}, "loadbalance": {}, }, + postProcess: breakForwardStubDomainsIntoServerBlocks, }, "1.4.0": { nextVersion: "1.5.0", @@ -242,6 +251,7 @@ var Versions = map[string]release{ "reload": {}, "loadbalance": {}, }, + postProcess: breakForwardStubDomainsIntoServerBlocks, }, "1.3.1": { nextVersion: "1.4.0", @@ -1056,3 +1066,43 @@ var proxyRemoveHttpsGoogleProtocol = func(o *corefile.Option) (*corefile.Option, } return o, nil } + +func breakForwardStubDomainsIntoServerBlocks(cf *corefile.Corefile) (*corefile.Corefile, error) { + for _, sb := range cf.Servers { + for j, fwd := range sb.Plugins { + if fwd.Name != "forward" { + continue + } + if len(fwd.Args) == 0 { + return nil, errors.New("found invalid forward plugin declaration") + } + if fwd.Args[0] == "." { + // dont move the default upstream + continue + } + if len(sb.DomPorts) != 1 { + return cf, errors.New("unhandled migration of multi-domain/port server block") + } + if sb.DomPorts[0] != "." && sb.DomPorts[0] != ".:53" { + return cf, errors.New("unhandled migration of non-default domain/port server block") + } + + newSb := &corefile.Server{} // create a new server block + newSb.DomPorts = []string{fwd.Args[0]} // copy the forward zone to the server block domain + fwd.Args[0] = "." // the plugin's zone changes to "." for brevity + newSb.Plugins = append(newSb.Plugins, fwd) // add the plugin to its new server block + + // Add appropriate addtl plugins to new server block + newSb.Plugins = append(newSb.Plugins, &corefile.Plugin{Name: "loop"}) + newSb.Plugins = append(newSb.Plugins, &corefile.Plugin{Name: "errors"}) + newSb.Plugins = append(newSb.Plugins, &corefile.Plugin{Name: "cache", Args: []string{"30"}}) + + //add new server block to corefile + cf.Servers = append(cf.Servers, newSb) + + //remove the forward plugin from the original server block + sb.Plugins = append(sb.Plugins[:j], sb.Plugins[j+1:]...) + } + } + return cf, nil +}