Skip to content

Commit

Permalink
Rework begin to simplify merge of Context
Browse files Browse the repository at this point in the history
This is a simplification and rework in response to networkservicemesh#1234

Signed-off-by: Ed Warnicke <hagbard@gmail.com>
  • Loading branch information
edwarnicke committed Mar 13, 2022
1 parent 8ddd0be commit 9805c46
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 52 deletions.
4 changes: 1 addition & 3 deletions pkg/networkservice/common/begin/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func (b *beginClient) Request(ctx context.Context, request *networkservice.Netwo
}

ctx = withEventFactory(ctx, eventFactoryClient)
request.Connection = mergeConnection(eventFactoryClient.returnedConnection, request.GetConnection(), eventFactoryClient.request.GetConnection())
request.Connection = mergeConnection(eventFactoryClient.request.GetConnection(), request.GetConnection())
conn, err = next.Client(ctx).Request(ctx, request, opts...)
if err != nil {
if eventFactoryClient.state != established {
Expand All @@ -80,8 +80,6 @@ func (b *beginClient) Request(ctx context.Context, request *networkservice.Netwo
eventFactoryClient.request.Connection = conn.Clone()
eventFactoryClient.opts = opts
eventFactoryClient.state = established

eventFactoryClient.returnedConnection = conn.Clone()
})
return conn, err
}
Expand Down
28 changes: 13 additions & 15 deletions pkg/networkservice/common/begin/event_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,13 @@ type EventFactory interface {
}

type eventFactoryClient struct {
state connectionState
executor serialize.Executor
ctxFunc func() (context.Context, context.CancelFunc)
request *networkservice.NetworkServiceRequest
returnedConnection *networkservice.Connection
opts []grpc.CallOption
client networkservice.NetworkServiceClient
afterCloseFunc func()
state connectionState
executor serialize.Executor
ctxFunc func() (context.Context, context.CancelFunc)
request *networkservice.NetworkServiceRequest
opts []grpc.CallOption
client networkservice.NetworkServiceClient
afterCloseFunc func()
}

func newEventFactoryClient(ctx context.Context, afterClose func(), opts ...grpc.CallOption) *eventFactoryClient {
Expand Down Expand Up @@ -142,13 +141,12 @@ func (f *eventFactoryClient) Close(opts ...Option) <-chan error {
var _ EventFactory = &eventFactoryClient{}

type eventFactoryServer struct {
state connectionState
executor serialize.Executor
ctxFunc func() (context.Context, context.CancelFunc)
request *networkservice.NetworkServiceRequest
returnedConnection *networkservice.Connection
afterCloseFunc func()
server networkservice.NetworkServiceServer
state connectionState
executor serialize.Executor
ctxFunc func() (context.Context, context.CancelFunc)
request *networkservice.NetworkServiceRequest
afterCloseFunc func()
server networkservice.NetworkServiceServer
}

func newEventFactoryServer(ctx context.Context, afterClose func()) *eventFactoryServer {
Expand Down
51 changes: 19 additions & 32 deletions pkg/networkservice/common/begin/merge.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,45 +21,32 @@ import (
"google.golang.org/protobuf/proto"
)

func mergeConnection(returnedConnection, requestedConnection, connection *networkservice.Connection) *networkservice.Connection {
if returnedConnection == nil || connection == nil {
// mergeConnection - deals explicitely with the problem of "What should we allow the 'main' of a client executable
// to update from outside the chain after the initial request for a connection.
// One minor point to keep in mind here is that a passthrough NSE, one that is a server that incorporates a client
// into its own chain, begin's at the server side... and so the server's use of client like functions are 'inside the chain'
func mergeConnection(returnedConnection, requestedConnection *networkservice.Connection) *networkservice.Connection {
// If this request has has yet to return successfully, go with the requesteConnection
if returnedConnection == nil {
return requestedConnection
}
conn := connection.Clone()

// Clone the previously returned connection
conn := returnedConnection.Clone()

// If the Request is asking for a new NSE, use that
if returnedConnection.GetNetworkServiceEndpointName() != requestedConnection.GetNetworkServiceEndpointName() {
conn.NetworkServiceEndpointName = requestedConnection.GetNetworkServiceEndpointName()
}
conn.Context = mergeConnectionContext(returnedConnection.GetContext(), requestedConnection.GetContext(), connection.GetContext())
return conn
}

func mergeConnectionContext(returnedConnectionContext, requestedConnectionContext, connectioncontext *networkservice.ConnectionContext) *networkservice.ConnectionContext {
rv := proto.Clone(connectioncontext).(*networkservice.ConnectionContext)
if !proto.Equal(returnedConnectionContext, requestedConnectionContext) {
// TODO: IPContext, DNSContext, EthernetContext, do we need to do MTU?
rv.ExtraContext = mergeMapStringString(returnedConnectionContext.GetExtraContext(), requestedConnectionContext.GetExtraContext(), connectioncontext.GetExtraContext())
// Ifthe Request is asking for a change in ConnectionContext, propagate that.
if !proto.Equal(returnedConnection.GetContext(), requestedConnection.GetContext()) {
conn.Context = proto.Clone(requestedConnection.GetContext()).(*networkservice.ConnectionContext)
}
return rv
}

func mergeMapStringString(returnedMap, requestedMap, mapMap map[string]string) map[string]string {
// clone the map
rv := make(map[string]string)
for k, v := range mapMap {
rv[k] = v
}

for k, v := range returnedMap {
requestedValue, ok := requestedMap[k]
// If a key is in returnedMap and its value differs from requestedMap, update the value
if ok && requestedValue != v {
rv[k] = requestedValue
}
// If a key is in returnedMap and not in requestedMap, delete it
if !ok {
delete(rv, k)
}
}
// Note: We are disallowing at this time changes in requested NetworkService, Mechanism, Labels, or Path.
// In the future it may be worth permitting changes in the Labels.
// It probably is not a good idea to allow changes in the NetworkService or Path here.

return rv
return conn
}
85 changes: 85 additions & 0 deletions pkg/networkservice/common/begin/rerequest_client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package begin_test

import (
"context"
"testing"

"github.com/networkservicemesh/api/pkg/api/networkservice"
"github.com/networkservicemesh/api/pkg/api/networkservice/mechanisms/kernel"
"github.com/stretchr/testify/require"
"go.uber.org/goleak"
"google.golang.org/grpc"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/emptypb"

"github.com/networkservicemesh/sdk/pkg/networkservice/common/begin"
"github.com/networkservicemesh/sdk/pkg/networkservice/core/chain"
)

func TestReRequestClient(t *testing.T) {
t.Cleanup(func() { goleak.VerifyNone(t) })

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

client := chain.NewNetworkServiceClient(
begin.NewClient(),
)

connOriginal, err := client.Request(ctx, &networkservice.NetworkServiceRequest{
Connection: &networkservice.Connection{
Id: "id",
},
})

require.NoError(t, err)
require.NotNil(t, connOriginal)

conn := connOriginal.Clone()
conn.Context = &networkservice.ConnectionContext{
IpContext: &networkservice.IPContext{
SrcIpAddrs: []string{"10.0.0.1/32"},
},
EthernetContext: &networkservice.EthernetContext{
SrcMac: "00:00:00:00:00:00",
},
DnsContext: &networkservice.DNSContext{
Configs: []*networkservice.DNSConfig{
{
DnsServerIps: []string{"1.1.1.1"},
},
},
},
ExtraContext: map[string]string{"foo": "bar"},
}
conn.Mechanism = kernel.New("")
conn.Labels = map[string]string{"foo": "bar"}

connReturned, err := client.Request(ctx, &networkservice.NetworkServiceRequest{
Connection: conn,
})

require.NoError(t, err)
require.NotNil(t, connReturned)
require.Equal(t, connOriginal.GetMechanism(), connReturned.GetMechanism())
require.True(t, proto.Equal(conn.GetContext(), connReturned.GetContext()))
require.Equal(t, connOriginal.GetLabels(), connReturned.GetLabels())
}

type reRequestClient struct{}

func newReRequestClient() networkservice.NetworkServiceClient {
return &reRequestClient{}
}

func (r *reRequestClient) Request(ctx context.Context, in *networkservice.NetworkServiceRequest, opts ...grpc.CallOption) (*networkservice.Connection, error) {
//TODO implement me
panic("implement me")
}

func (r *reRequestClient) Close(ctx context.Context, in *networkservice.Connection, opts ...grpc.CallOption) (*emptypb.Empty, error) {
//TODO implement me
panic("implement me")
}

var _ networkservice.NetworkServiceClient = &reRequestClient{}
2 changes: 0 additions & 2 deletions pkg/networkservice/common/begin/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,6 @@ func (b *beginServer) Request(ctx context.Context, request *networkservice.Netwo
eventFactoryServer.request = request.Clone()
eventFactoryServer.request.Connection = conn.Clone()
eventFactoryServer.state = established

eventFactoryServer.returnedConnection = conn.Clone()
})
return conn, err
}
Expand Down
43 changes: 43 additions & 0 deletions pkg/networkservice/common/dial/dial_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package dial_test

import (
"context"
"net"
"testing"
"time"

"google.golang.org/grpc"

"github.com/networkservicemesh/api/pkg/api/networkservice"
)

func TestDial(t *testing.T) {
var dialer net.Dialer
conn, err := dialer.DialContext(context.Background(), "tcp", "142.251.1.106:53")
if err != nil {
panic(err.Error())
}
if conn == nil {
panic(conn)
}
}

func TestDial2(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
defer cancel()
cc, err := grpc.DialContext(ctx, "142.251.1.106:53", grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
panic(err.Error())
}
if cc == nil {
panic(cc)
}
client := networkservice.NewNetworkServiceClient(cc)
conn, err := client.Request(context.Background(), &networkservice.NetworkServiceRequest{})
if err != nil {
panic(err.Error())
}
if conn == nil {
panic(cc)
}
}

0 comments on commit 9805c46

Please sign in to comment.