Skip to content

Commit

Permalink
feat(kuma-dp) add coredns server to kuma-dp (#1811)
Browse files Browse the repository at this point in the history
* feat(kuma-dp) add functionality to run dns server

Signed-off-by: Bart Smykla <bartek@smykla.com>

* feat(*) add building coredns to workflow

Signed-off-by: Bart Smykla <bartek@smykla.com>
(cherry picked from commit 49c85ea)

# Conflicts:
#	app/kuma-dp/cmd/run.go
#	pkg/config/app/kuma-dp/config.go
#	pkg/config/app/kuma-dp/config_test.go
#	pkg/config/app/kuma-dp/testdata/default-config.golden.yaml
  • Loading branch information
bartsmykla authored and mergify-bot committed Apr 14, 2021
1 parent ab2e601 commit aa2515c
Show file tree
Hide file tree
Showing 17 changed files with 673 additions and 10 deletions.
63 changes: 57 additions & 6 deletions app/kuma-dp/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import (
"path/filepath"

kumadp_config "github.com/kumahq/kuma/app/kuma-dp/pkg/config"
"github.com/kumahq/kuma/app/kuma-dp/pkg/dataplane/dnsserver"
"github.com/kumahq/kuma/pkg/core/resources/model/rest"
"github.com/kumahq/kuma/pkg/core/runtime/component"

"github.com/pkg/errors"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -74,14 +76,22 @@ func newRunCmd(rootCtx *RootContext) *cobra.Command {
runLog.Info("picked a free port for Envoy Admin API to listen on", "port", cfg.Dataplane.AdminPort)
}

if cfg.DataplaneRuntime.ConfigDir == "" {
if cfg.DataplaneRuntime.ConfigDir == "" || cfg.DNS.ConfigDir == "" {
tmpDir, err = ioutil.TempDir("", "kuma-dp-")
if err != nil {
runLog.Error(err, "unable to create a temporary directory to store generated Envoy config at")
runLog.Error(err, "unable to create a temporary directory to store generated config sat")
return err
}
cfg.DataplaneRuntime.ConfigDir = tmpDir
runLog.Info("generated Envoy configuration will be stored in a temporary directory", "dir", tmpDir)

if cfg.DataplaneRuntime.ConfigDir == "" {
cfg.DataplaneRuntime.ConfigDir = tmpDir
}

if cfg.DNS.ConfigDir == "" {
cfg.DNS.ConfigDir = tmpDir
}

runLog.Info("generated configurations will be stored in a temporary directory", "dir", tmpDir)
}

if cfg.DataplaneRuntime.Token != "" {
Expand Down Expand Up @@ -116,7 +126,11 @@ func newRunCmd(rootCtx *RootContext) *cobra.Command {
}
}()
}

shouldQuit := setupQuitChannel()
components := []component.Component{
accesslogs.NewAccessLogServer(cfg.Dataplane),
}

dataplane, err := envoy.New(envoy.Opts{
Config: *cfg,
Expand All @@ -127,13 +141,39 @@ func newRunCmd(rootCtx *RootContext) *cobra.Command {
Stderr: cmd.OutOrStderr(),
Quit: shouldQuit,
LogLevel: rootCtx.LogLevel,
<<<<<<< HEAD
})
=======
}

if cfg.DNS.Enabled {
opts.DNSPort = cfg.DNS.EnvoyDNSPort
opts.EmptyDNSPort = cfg.DNS.CoreDNSEmptyPort

dnsOpts := &dnsserver.Opts{
Config: *cfg,
Stdout: cmd.OutOrStdout(),
Stderr: cmd.OutOrStderr(),
Quit: shouldQuit,
}

dnsServer, err := dnsserver.New(dnsOpts)
if err != nil {
return err
}

components = append(components, dnsServer)
}

dataplane, err := envoy.New(opts)
>>>>>>> 49c85eaf8... feat(kuma-dp) add coredns server to kuma-dp (#1811)
if err != nil {
return err
}
server := accesslogs.NewAccessLogServer(cfg.Dataplane)

if err := rootCtx.ComponentManager.Add(server, dataplane); err != nil {
components = append(components, dataplane)

if err := rootCtx.ComponentManager.Add(components...); err != nil {
return err
}

Expand All @@ -160,6 +200,17 @@ func newRunCmd(rootCtx *RootContext) *cobra.Command {
cmd.PersistentFlags().StringVar(&cfg.DataplaneRuntime.Resource, "dataplane", "", "Dataplane template to apply (YAML or JSON)")
cmd.PersistentFlags().StringVarP(&cfg.DataplaneRuntime.ResourcePath, "dataplane-file", "d", "", "Path to Dataplane template to apply (YAML or JSON)")
cmd.PersistentFlags().StringToStringVarP(&cfg.DataplaneRuntime.ResourceVars, "dataplane-var", "v", map[string]string{}, "Variables to replace Dataplane template")
<<<<<<< HEAD
=======
cmd.PersistentFlags().BoolVar(&cfg.DNS.Enabled, "dns-enabled", cfg.DNS.Enabled, "If true then builtin DNS functionality is enabled and CoreDNS server is started")
cmd.PersistentFlags().Uint32Var(&cfg.DNS.EnvoyDNSPort, "dns-envoy-port", cfg.DNS.EnvoyDNSPort, "A port that handles Virtual IP resolving by Envoy. CoreDNS should be configured that it first tries to use this DNS resolver and then the real one")
cmd.PersistentFlags().Uint32Var(&cfg.DNS.CoreDNSPort, "dns-coredns-port", cfg.DNS.CoreDNSPort, "A port that handles DNS requests. When transparent proxy is enabled then iptables will redirect DNS traffic to this port.")
cmd.PersistentFlags().Uint32Var(&cfg.DNS.CoreDNSEmptyPort, "dns-coredns-empty-port", cfg.DNS.CoreDNSEmptyPort, "A port that always responds with empty NXDOMAIN respond. It is required to implement a fallback to a real DNS.")
cmd.PersistentFlags().StringVar(&cfg.DNS.CoreDNSBinaryPath, "dns-coredns-path", cfg.DNS.CoreDNSBinaryPath, "A path to CoreDNS binary.")
cmd.PersistentFlags().StringVar(&cfg.DNS.CoreDNSConfigTemplatePath, "dns-coredns-config-template-path", cfg.DNS.CoreDNSConfigTemplatePath, "A path to a CoreDNS config template.")
cmd.PersistentFlags().StringVar(&cfg.DNS.ConfigDir, "dns-server-config-dir", cfg.DNS.ConfigDir, "Directory in which DNS Server config will be generated")
cmd.PersistentFlags().Uint32Var(&cfg.DNS.PrometheusPort, "dns-prometheus-port", cfg.DNS.PrometheusPort, "A port for exposing Prometheus stats")
>>>>>>> 49c85eaf8... feat(kuma-dp) add coredns server to kuma-dp (#1811)
return cmd
}

Expand Down
26 changes: 26 additions & 0 deletions app/kuma-dp/pkg/dataplane/dnsserver/config_file.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package dnsserver

import (
"io/ioutil"
"os"
"path/filepath"

"github.com/pkg/errors"

kuma_dp "github.com/kumahq/kuma/pkg/config/app/kuma-dp"
)

func GenerateConfigFile(cfg kuma_dp.DNS, config []byte) (string, error) {
configFile := filepath.Join(cfg.ConfigDir, "Corefile")
if err := writeFile(configFile, config, 0600); err != nil {
return "", errors.Wrap(err, "failed to persist Envoy bootstrap config on disk")
}
return configFile, nil
}

func writeFile(filename string, data []byte, perm os.FileMode) error {
if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil {
return err
}
return ioutil.WriteFile(filename, data, perm)
}
60 changes: 60 additions & 0 deletions app/kuma-dp/pkg/dataplane/dnsserver/config_file_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package dnsserver

import (
"io/ioutil"
"os"
"path/filepath"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"

kuma_dp "github.com/kumahq/kuma/pkg/config/app/kuma-dp"
)

var _ = Describe("Config File", func() {
Describe("GenerateConfigFile(..)", func() {

var configDir string

BeforeEach(func() {
var err error
configDir, err = ioutil.TempDir("", "")
Expect(err).ToNot(HaveOccurred())
})
AfterEach(func() {
if configDir != "" {
// when
err := os.RemoveAll(configDir)
// then
Expect(err).ToNot(HaveOccurred())
}
})

It("should create DNS Server config file on disk", func() {
// given
config := `. {
errors
}`
// and
dnsConfig := kuma_dp.DNS{
ConfigDir: configDir,
}

// when
filename, err := GenerateConfigFile(dnsConfig, []byte(config))
// then
Expect(err).ToNot(HaveOccurred())
// and
Expect(filename).To(Equal(filepath.Join(configDir, "Corefile")))

// when
actual, err := ioutil.ReadFile(filename)
// then
Expect(err).ToNot(HaveOccurred())
// and
Expect(actual).To(Equal([]byte(`. {
errors
}`)))
})
})
})
184 changes: 184 additions & 0 deletions app/kuma-dp/pkg/dataplane/dnsserver/dnsserver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
package dnsserver

import (
"bytes"
"context"
"io"
"os"
"os/exec"
"path/filepath"
"text/template"

"github.com/pkg/errors"

kuma_dp "github.com/kumahq/kuma/pkg/config/app/kuma-dp"
"github.com/kumahq/kuma/pkg/core"
)

var (
runLog = core.Log.WithName("kuma-dp").WithName("run").WithName("dns-server")
)

type DNSServer struct {
opts *Opts
}

type Opts struct {
Config kuma_dp.Config
Stdout io.Writer
Stderr io.Writer
Quit chan struct{}
}

const DefaultCoreFileTemplate = `.:{{ .CoreDNSPort }} {
forward . 127.0.0.1:{{ .EnvoyDNSPort }}
alternate NXDOMAIN,SERVFAIL,REFUSED . /etc/resolv.conf
prometheus localhost:{{ .PrometheusPort }}
errors
}
.:{{ .CoreDNSEmptyPort }} {
template ANY ANY . {
rcode NXDOMAIN
}
}`

func getSelfPath() (string, error) {
ex, err := os.Executable()
if err != nil {
return "", err
}

return filepath.Dir(ex), nil
}

func lookupBinaryPath(candidatePaths []string) (string, error) {
for _, candidatePath := range candidatePaths {
path, err := exec.LookPath(candidatePath)
if err == nil {
return path, nil
}
}

return "", errors.Errorf("could not find binary in any of the following paths: %v", candidatePaths)
}

func lookupDNSServerPath(configuredPath string) (string, error) {
selfPath, err := getSelfPath()
if err != nil {
return "", err
}

cwd, err := os.Getwd()
if err != nil {
return "", err
}

path, err := lookupBinaryPath([]string{
configuredPath,
selfPath + "/coredns",
cwd + "/coredns",
})
if err != nil {
return "", err
}

return path, nil
}

func New(opts *Opts) (*DNSServer, error) {
if _, err := lookupDNSServerPath(opts.Config.DNS.CoreDNSBinaryPath); err != nil {
runLog.Error(err, "could not find the DNS Server executable in your path")
return nil, err
}

return &DNSServer{opts: opts}, nil
}

func (s *DNSServer) NeedLeaderElection() bool {
return false
}

func (s *DNSServer) Start(stop <-chan struct{}) error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

dnsConfig := s.opts.Config.DNS

var tmpl *template.Template

if dnsConfig.CoreDNSConfigTemplatePath != "" {
t, err := template.ParseFiles(dnsConfig.CoreDNSConfigTemplatePath)
if err != nil {
return err
}

tmpl = t
} else {
t, err := template.New("Corefile").Parse(DefaultCoreFileTemplate)
if err != nil {
return err
}

tmpl = t
}

bs := bytes.NewBuffer([]byte{})

if err := tmpl.Execute(bs, dnsConfig); err != nil {
return err
}

configFile, err := GenerateConfigFile(dnsConfig, bs.Bytes())
if err != nil {
return err
}
runLog.Info("configuration saved to a file", "file", configFile)

binaryPathConfig := dnsConfig.CoreDNSBinaryPath
resolvedPath, err := lookupDNSServerPath(binaryPathConfig)
if err != nil {
return err
}

args := []string{
"-conf", configFile,
"-q",
}

command := exec.CommandContext(ctx, resolvedPath, args...)
command.Stdout = s.opts.Stdout
command.Stderr = s.opts.Stderr

runLog.Info("starting DNS Server (coredns)", "args", args)

if err := command.Start(); err != nil {
runLog.Error(err, "the DNS Server executable was found at "+resolvedPath+" but an error occurred when executing it")
return err
}

done := make(chan error, 1)

go func() {
done <- command.Wait()
}()

select {
case <-stop:
runLog.Info("stopping DNS Server")
cancel()
return nil
case err := <-done:
if err != nil {
runLog.Error(err, "DNS Server terminated with an error")
} else {
runLog.Info("DNS Server terminated successfully")
}

if s.opts.Quit != nil {
close(s.opts.Quit)
}

return err
}
}
13 changes: 13 additions & 0 deletions app/kuma-dp/pkg/dataplane/dnsserver/dnsserver_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package dnsserver

import (
"testing"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

func TestEnvoy(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "DNS Server Suite")
}
Loading

0 comments on commit aa2515c

Please sign in to comment.