coredns / rfc

Propsals and request for comments for CoreDNS
0 stars 11 forks source link

proposal: Embeddable plugin #6

Closed Xuanwo closed 4 years ago

Xuanwo commented 4 years ago

Background

I'm trying to build a plugin that can take action depending on different conditions. For example, the following config will forward dns query to 127.0.0.1:5353 while condition domain_in_file domain_with_local_cdn.txt is satisfied.

policy {
   condition domain_in_file domain_with_local_cdn.txt
   action forward . 127.0.0.1:5353
}

However, I found it's hard to reuse existing plugin forward.

First of all, to support plugin defined config, coredns let plugin to parse config by themself. Such as forward:

func parseForward(c *caddy.Controller) (*Forward, error) {
   var (
      f   *Forward
      err error
      i   int
   )
   for c.Next() {
      if i > 0 {
         return nil, plugin.ErrOnce
      }
      i++
      f, err = parseStanza(c)
      if err != nil {
         return nil, err
      }
   }
   return f, nil
}

This function isn't exported so we can't pass the forward config to build a new forward plugin instance.

Secondly, forward doesn't provide a configurable initialization function:

// New returns a new Forward.
func New() *Forward {
   f := &Forward{maxfails: 2, tlsConfig: new(tls.Config), expire: defaultExpire, p: new(policy.Random), from: ".", hcInterval: hcInterval, opts: options{forceTCP: false, preferUDP: false, hcRecursionDesired: true}}
   return f
}

// SetProxy appends p to the proxy list and starts healthchecking.
func (f *Forward) SetProxy(p *Proxy) {
   f.proxies = append(f.proxies, p)
   p.start(f.hcInterval)
}

No arguments for New() and only SetProxy is supported. So it's harder for other plugins to reuse forward.

Motivation

Embeddable plugin allows the plugin to be reusable.

Proposal

Relate

proxy in fallback

fallback is an external plugin allow redirecting queries to an alternate set of upstreams based on RCODE.

It's reuse proxy via proxy.NewStaticUpstream:

func setup(c *caddy.Controller) error {
   ...

   for c.Next() {
      ..

      u, err := proxy.NewStaticUpstream(&c.Dispenser)
      if err != nil {
         return plugin.Error("fallback", err)
      }

      ...
   }
    ...

   return nil
}

https://github.com/coredns/fallback/blob/master/setup.go

fanout

fanout is an external plugin that parallel proxying DNS messages to upstream resolvers.

Without embed support, they have to implement a dns client by themself:

func (c *client) Request(r *request.Request) (*dns.Msg, error) {
   start := time.Now()
   conn, err := c.transport.Dial(c.net)
   if err != nil {
      return nil, err
   }
   defer func() {
      logErrIfNotNil(conn.Close())
   }()

   logErrIfNotNil(conn.SetWriteDeadline(time.Now().Add(maxTimeout)))
   if err = conn.WriteMsg(r.Req); err != nil {
      logErrIfNotNil(err)
      return nil, err
   }
   logErrIfNotNil(conn.SetReadDeadline(time.Now().Add(readTimeout)))
   var ret *dns.Msg
   for {
      ret, err = conn.ReadMsg()
      if err != nil {
         logErrIfNotNil(err)
         return nil, err
      }
      if r.Req.Id == ret.Id {
         break
      }
   }
   rc, ok := dns.RcodeToString[ret.Rcode]
   if !ok {
      rc = fmt.Sprint(ret.Rcode)
   }
   RequestCount.WithLabelValues(c.addr).Add(1)
   RcodeCount.WithLabelValues(rc, c.addr).Add(1)
   RequestDuration.WithLabelValues(c.addr).Observe(time.Since(start).Seconds())
   return ret, nil
}

https://github.com/networkservicemesh/fanout/blob/master/client.go

kubernetai

kubernetai serves multiple Kubernetes within a Server which is another example for the embeddable plugin.

They don't need to parse kubernetes config anymore:

func Parse(c *caddy.Controller) (*Kubernetai, error) {
   ...

   for c.Next() {
      var k8s *kubernetes.Kubernetes
      k8s, err = kubernetes.ParseStanza(c)
      if err != nil {
         return nil, err
      }
      k8i.Kubernetes = append(k8i.Kubernetes, k8s)
   }

    ...

   return k8i, nil
}

https://github.com/coredns/kubernetai/blob/master/plugin/kubernetai/setup.go

Xuanwo commented 4 years ago

Closed for no response.