Skip to content

Commit

Permalink
feat: End to end DNS integration: Update dnscontext/client and dnscon…
Browse files Browse the repository at this point in the history
…text/server to latest changes of SDK (networkservicemesh#866)

* fix issues networkservicemesh#825 and networkservicemesh#836

Signed-off-by: Denis Tingaikin <denis.tingajkin@xored.com>

* add decoder

Signed-off-by: denis-tingajkin <denis.tingajkin@xored.com>

* fix test fail on ci

Signed-off-by: denis-tingajkin <denis.tingajkin@xored.com>

* fix issue with missed coredns dir

Signed-off-by: Denis Tingaikin <denis.tingajkin@xored.com>

* do not add dnsclient into chains/client

Signed-off-by: denis-tingajkin <denis.tingajkin@xored.com>
  • Loading branch information
denis-tingaikin authored and Vladimir Popov committed Apr 26, 2021
1 parent 26410c2 commit b79bd66
Show file tree
Hide file tree
Showing 11 changed files with 369 additions and 236 deletions.
99 changes: 99 additions & 0 deletions pkg/networkservice/chains/nsmgr/nsmgr_single_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright (c) 2021 Doc.ai and/or its affiliates.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package nsmgr_test

import (
"context"
"io/ioutil"
"os"
"path/filepath"
"testing"
"time"

"github.com/networkservicemesh/api/pkg/api/networkservice"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"

"github.com/networkservicemesh/sdk/pkg/networkservice/chains/client"
"github.com/networkservicemesh/sdk/pkg/networkservice/common/authorize"
"github.com/networkservicemesh/sdk/pkg/networkservice/connectioncontext/dnscontext"
"github.com/networkservicemesh/sdk/pkg/tools/grpcutils"
"github.com/networkservicemesh/sdk/pkg/tools/sandbox"
)

func Test_DNSUsecase(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()

var cluster = sandbox.NewBuilder(t).SetNodesCount(1).SetNSMgrProxySupplier(nil).SetRegistryProxySupplier(nil).SetContext(ctx).Build()

_, err := cluster.Nodes[0].NewEndpoint(ctx, defaultRegistryEndpoint(), sandbox.GenerateTestToken, dnscontext.NewServer(
&networkservice.DNSConfig{
DnsServerIps: []string{"8.8.8.8"},
SearchDomains: []string{"my.domain1"},
},
&networkservice.DNSConfig{
DnsServerIps: []string{"8.8.4.4"},
SearchDomains: []string{"my.domain1"},
},
))
require.NoError(t, err)

corefilePath := filepath.Join(t.TempDir(), "corefile")
resolveConfigPath := filepath.Join(t.TempDir(), "resolv.conf")

err = ioutil.WriteFile(resolveConfigPath, []byte("nameserver 8.8.4.4\nsearch example.com\n"), os.ModePerm)
require.NoError(t, err)

const expectedCorefile = ". {\n\tforward . 8.8.4.4\n\tlog\n\treload\n}\nmy.domain1 {\n\tfanout . 8.8.4.4 8.8.8.8\n\tlog\n}"

cc, err := grpc.DialContext(ctx, grpcutils.URLToTarget(cluster.Nodes[0].NSMgr.URL), sandbox.DefaultDialOptions(sandbox.GenerateTestToken)...)
require.NoError(t, err)

t.Cleanup(func() {
_ = cc.Close()
})

c := client.NewClient(
ctx,
cc,
client.WithAuthorizeClient(authorize.NewClient(authorize.Any())),
client.WithAdditionalFunctionality(
dnscontext.NewClient(
dnscontext.WithChainContext(ctx),
dnscontext.WithCorefilePath(corefilePath),
dnscontext.WithResolveConfigPath(resolveConfigPath),
),
),
)

resp, err := c.Request(ctx, defaultRequest())
require.NoError(t, err)

require.Eventually(t, func() bool {
// #nosec
b, readFileErr := ioutil.ReadFile(corefilePath)
if readFileErr != nil {
return false
}
return string(b) == expectedCorefile
}, time.Second, time.Millisecond*100)
_, err = c.Close(ctx, resp)
require.NoError(t, err)
_, err = cluster.Nodes[0].EndpointRegistryClient.Unregister(ctx, defaultRegistryEndpoint())
require.NoError(t, err)
}
108 changes: 48 additions & 60 deletions pkg/networkservice/connectioncontext/dnscontext/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,39 +20,35 @@ import (
"context"
"io/ioutil"
"os"
"path/filepath"
"sync"

"github.com/edwarnicke/serialize"

"github.com/networkservicemesh/sdk/pkg/tools/log"

"github.com/golang/protobuf/ptypes/empty"
"github.com/networkservicemesh/api/pkg/api/networkservice"
"google.golang.org/grpc"

"github.com/edwarnicke/serialize"

"github.com/networkservicemesh/sdk/pkg/networkservice/core/next"
"github.com/networkservicemesh/sdk/pkg/tools/dnscontext"
)

type dnsContextClient struct {
cancelMonitoring context.CancelFunc
chainContext context.Context
monitorContext context.Context
requestContext context.Context
coreFilePath string
resolveConfigPath string
defaultNameServerIP string
monitorCallOptions []grpc.CallOption
dnsConfigManager dnscontext.Manager
monitorClient networkservice.MonitorConnectionClient
executor serialize.Executor
updateCorefileQueue serialize.Executor
once sync.Once
}

// NewClient creates a new DNS client chain component. Setups all DNS traffic to the localhost. Monitors DNS configs from connections.
func NewClient(monitorClient networkservice.MonitorConnectionClient, options ...DNSOption) networkservice.NetworkServiceClient {
func NewClient(options ...DNSOption) networkservice.NetworkServiceClient {
c := &dnsContextClient{
chainContext: context.Background(),
dnsConfigManager: dnscontext.NewManager(),
monitorClient: monitorClient,
defaultNameServerIP: "127.0.0.1",
resolveConfigPath: "/etc/resolv.conf",
coreFilePath: "/etc/coredns/Corefile",
Expand All @@ -61,18 +57,6 @@ func NewClient(monitorClient networkservice.MonitorConnectionClient, options ...
o.apply(c)
}

if r, err := dnscontext.OpenResolveConfig(c.resolveConfigPath); err != nil {
log.FromContext(c.chainContext).Errorf("DnsContextClient: can not load resolve config file. Path: %v. Error: %v", c.resolveConfigPath, err.Error())
} else {
c.dnsConfigManager.Store("", &networkservice.DNSConfig{
SearchDomains: r.Value(dnscontext.AnyDomain),
DnsServerIps: r.Value(dnscontext.NameserverProperty),
})
r.SetValue(dnscontext.NameserverProperty, c.defaultNameServerIP)
if err := r.Save(); err != nil {
log.FromContext(c.chainContext).Errorf("DnsContextClient: can not update resolve config file. Error: %v", err.Error())
}
}
return c
}

Expand All @@ -81,53 +65,57 @@ func (c *dnsContextClient) Request(ctx context.Context, request *networkservice.
if err != nil {
return nil, err
}
c.requestContext = ctx
c.executor.AsyncExec(c.monitorConfigs)
var conifgs []*networkservice.DNSConfig
if rv.GetContext().GetDnsContext() != nil {
conifgs = rv.GetContext().GetDnsContext().GetConfigs()
}
if len(conifgs) > 0 {
c.once.Do(c.initialize)
c.dnsConfigManager.Store(rv.GetId(), conifgs...)
c.updateCorefileQueue.AsyncExec(c.updateCorefile)
}
return rv, err
}

func (c *dnsContextClient) Close(ctx context.Context, conn *networkservice.Connection, opts ...grpc.CallOption) (*empty.Empty, error) {
r, err := next.Client(ctx).Close(ctx, conn, opts...)
func (c *dnsContextClient) updateCorefile() {
dir := filepath.Dir(c.coreFilePath)
_ = os.MkdirAll(dir, os.ModePerm)
err := ioutil.WriteFile(c.coreFilePath, []byte(c.dnsConfigManager.String()), os.ModePerm)
if err != nil {
return nil, err
log.FromContext(c.chainContext).Errorf("An error during update corefile: %v", err.Error())
}
c.cancelMonitoring()
return r, err
}

func (c *dnsContextClient) monitorConfigs() {
c.monitorContext, c.cancelMonitoring = context.WithCancel(c.chainContext)
steam, err := c.monitorClient.MonitorConnections(c.monitorContext, &networkservice.MonitorScopeSelector{}, c.monitorCallOptions...)
if err != nil {
log.FromContext(c.requestContext).Errorf("DnsContextClient: Can not start monitor connections: %v", err)
c.executor.AsyncExec(c.monitorConfigs)
return
func (c *dnsContextClient) Close(ctx context.Context, conn *networkservice.Connection, opts ...grpc.CallOption) (*empty.Empty, error) {
var conifgs []*networkservice.DNSConfig
if conn.GetContext().GetDnsContext() != nil {
conifgs = conn.GetContext().GetDnsContext().GetConfigs()
}
for {
if c.monitorContext.Err() != nil {
return
}
event, err := steam.Recv()
if err != nil {
c.executor.AsyncExec(c.monitorConfigs)
return
}
c.handleEvent(event)
v := c.dnsConfigManager.String()
log.FromContext(c.requestContext).Info(v)
_ = ioutil.WriteFile(c.coreFilePath, []byte(v), os.ModePerm)
if len(conifgs) > 0 {
c.dnsConfigManager.Remove(conn.GetId())
c.updateCorefileQueue.AsyncExec(c.updateCorefile)
}
return next.Client(ctx).Close(ctx, conn, opts...)
}

func (c *dnsContextClient) handleEvent(event *networkservice.ConnectionEvent) {
switch event.GetType() {
case networkservice.ConnectionEventType_INITIAL_STATE_TRANSFER, networkservice.ConnectionEventType_UPDATE:
for k, v := range event.Connections {
c.dnsConfigManager.Store(k, v.GetContext().GetDnsContext().GetConfigs()...)
}
case networkservice.ConnectionEventType_DELETE:
for k := range event.Connections {
c.dnsConfigManager.Remove(k)
}
func (c *dnsContextClient) initialize() {
r, err := dnscontext.OpenResolveConfig(c.resolveConfigPath)
if err != nil {
log.FromContext(c.chainContext).Errorf("An error during open resolve config: %v", err.Error())
return
}

c.dnsConfigManager.Store("", &networkservice.DNSConfig{
SearchDomains: r.Value(dnscontext.AnyDomain),
DnsServerIps: r.Value(dnscontext.NameserverProperty),
})

r.SetValue(dnscontext.NameserverProperty, c.defaultNameServerIP)

if err = r.Save(); err != nil {
log.FromContext(c.chainContext).Errorf("An error during save resolve config: %v", err.Error())
return
}

c.updateCorefileQueue.AsyncExec(c.updateCorefile)
}
Loading

0 comments on commit b79bd66

Please sign in to comment.