mirror of
https://github.com/coredns/coredns.git
synced 2025-11-12 23:12:17 -05:00
Initial implementation of ForwardCRD plugin (#4512)
* Add forwardcrd plugin README.md Co-authored-by: Aidan Obley <aobley@vmware.com> Signed-off-by: Christian Ang <angc@vmware.com> * Create forwardcrd plugin - Place forwardcrd before forward plugin in plugin list. This will avoid forward from preventing the forwardcrd plugin from handling any queries in the case of having a default upstream forwarder in a server block (as is the case in the default kubernetes Corefile). Co-authored-by: Aidan Obley <aobley@vmware.com> Signed-off-by: Christian Ang <angc@vmware.com> * Add Forward CRD Signed-off-by: Christian Ang <angc@vmware.com> * Add NewWithConfig to forward plugin - allows external packages to instanciate forward plugins Co-authored-by: Aidan Obley <aobley@vmware.com> Signed-off-by: Christian Ang <angc@vmware.com> * ForwardCRD plugin handles requests for Forward CRs - add a Kubernetes controller that can read Forward CRs - instances of the forward plugin are created based on Forward CRs from the Kubernetes controller - DNS requests are handled by calling matching Forward plugin instances based on zone name - Defaults to the kube-system namespace to align with Corefile RBAC Signed-off-by: Christian Ang <angc@vmware.com> Use klog v2 in forwardcrd plugin * Refactor forward setup to use NewWithConfig Co-authored-by: Christian Ang <angc@vmware.com> Signed-off-by: Edwin Xie <exie@vmware.com> * Use ParseInt instead of Atoi - to ensure that the bitsize is 32 for later casting to uint32 Signed-off-by: Christian Ang <angc@vmware.com> * Add @christianang to CODEOWNERS for forwardcrd Signed-off-by: Christian Ang <angc@vmware.com> Co-authored-by: Edwin Xie <exie@vmware.com>
This commit is contained in:
162
plugin/forwardcrd/forwardcrd.go
Normal file
162
plugin/forwardcrd/forwardcrd.go
Normal file
@@ -0,0 +1,162 @@
|
||||
package forwardcrd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/coredns/coredns/plugin"
|
||||
"github.com/coredns/coredns/plugin/forward"
|
||||
corednsv1alpha1 "github.com/coredns/coredns/plugin/forwardcrd/apis/coredns/v1alpha1"
|
||||
"github.com/coredns/coredns/request"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
// ForwardCRD represents a plugin instance that can watch Forward CRDs
|
||||
// within a Kubernetes clusters to dynamically configure stub-domains to proxy
|
||||
// requests to an upstream resolver.
|
||||
type ForwardCRD struct {
|
||||
Zones []string
|
||||
APIServerEndpoint string
|
||||
APIClientCert string
|
||||
APIClientKey string
|
||||
APICertAuth string
|
||||
Namespace string
|
||||
ClientConfig clientcmd.ClientConfig
|
||||
APIConn forwardCRDController
|
||||
Next plugin.Handler
|
||||
|
||||
pluginInstanceMap *PluginInstanceMap
|
||||
}
|
||||
|
||||
// New returns a new ForwardCRD instance.
|
||||
func New() *ForwardCRD {
|
||||
return &ForwardCRD{
|
||||
Namespace: "kube-system",
|
||||
|
||||
pluginInstanceMap: NewPluginInstanceMap(),
|
||||
}
|
||||
}
|
||||
|
||||
// Name implements plugin.Handler.
|
||||
func (k *ForwardCRD) Name() string { return "forwardcrd" }
|
||||
|
||||
// ServeDNS implements plugin.Handler.
|
||||
func (k *ForwardCRD) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
||||
question := strings.ToLower(r.Question[0].Name)
|
||||
|
||||
state := request.Request{W: w, Req: r}
|
||||
if !k.match(state) {
|
||||
return plugin.NextOrFailure(k.Name(), k.Next, ctx, w, r)
|
||||
}
|
||||
|
||||
var (
|
||||
offset int
|
||||
end bool
|
||||
)
|
||||
|
||||
for {
|
||||
p, ok := k.pluginInstanceMap.Get(question[offset:])
|
||||
if ok {
|
||||
a, b := p.ServeDNS(ctx, w, r)
|
||||
return a, b
|
||||
}
|
||||
|
||||
offset, end = dns.NextLabel(question, offset)
|
||||
if end {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return plugin.NextOrFailure(k.Name(), k.Next, ctx, w, r)
|
||||
}
|
||||
|
||||
// Ready implements the ready.Readiness interface
|
||||
func (k *ForwardCRD) Ready() bool {
|
||||
return k.APIConn.HasSynced()
|
||||
}
|
||||
|
||||
// InitKubeCache initializes a new Kubernetes cache.
|
||||
func (k *ForwardCRD) InitKubeCache(ctx context.Context) error {
|
||||
config, err := k.getClientConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dynamicKubeClient, err := dynamic.NewForConfig(config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create forwardcrd controller: %q", err)
|
||||
}
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
err = corednsv1alpha1.AddToScheme(scheme)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create forwardcrd controller: %q", err)
|
||||
}
|
||||
|
||||
k.APIConn = newForwardCRDController(ctx, dynamicKubeClient, scheme, k.Namespace, k.pluginInstanceMap, func(cfg forward.ForwardConfig) (lifecyclePluginHandler, error) {
|
||||
return forward.NewWithConfig(cfg)
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *ForwardCRD) getClientConfig() (*rest.Config, error) {
|
||||
if k.ClientConfig != nil {
|
||||
return k.ClientConfig.ClientConfig()
|
||||
}
|
||||
loadingRules := &clientcmd.ClientConfigLoadingRules{}
|
||||
overrides := &clientcmd.ConfigOverrides{}
|
||||
clusterinfo := clientcmdapi.Cluster{}
|
||||
authinfo := clientcmdapi.AuthInfo{}
|
||||
|
||||
// Connect to API from in cluster
|
||||
if k.APIServerEndpoint == "" {
|
||||
cc, err := rest.InClusterConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc.ContentType = "application/vnd.kubernetes.protobuf"
|
||||
return cc, err
|
||||
}
|
||||
|
||||
// Connect to API from out of cluster
|
||||
clusterinfo.Server = k.APIServerEndpoint
|
||||
|
||||
if len(k.APICertAuth) > 0 {
|
||||
clusterinfo.CertificateAuthority = k.APICertAuth
|
||||
}
|
||||
if len(k.APIClientCert) > 0 {
|
||||
authinfo.ClientCertificate = k.APIClientCert
|
||||
}
|
||||
if len(k.APIClientKey) > 0 {
|
||||
authinfo.ClientKey = k.APIClientKey
|
||||
}
|
||||
|
||||
overrides.ClusterInfo = clusterinfo
|
||||
overrides.AuthInfo = authinfo
|
||||
clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, overrides)
|
||||
|
||||
cc, err := clientConfig.ClientConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc.ContentType = "application/vnd.kubernetes.protobuf"
|
||||
return cc, err
|
||||
}
|
||||
|
||||
func (k *ForwardCRD) match(state request.Request) bool {
|
||||
for _, zone := range k.Zones {
|
||||
if plugin.Name(zone).Matches(state.Name()) || dns.Name(state.Name()) == dns.Name(zone) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user