Skip to content

Commit

Permalink
Add replaceNSEName chain element (#1328)
Browse files Browse the repository at this point in the history
* Add replaceNSEName chain element

Signed-off-by: Artem Glazychev <artem.glazychev@xored.com>

* Create passthrough chain element

Signed-off-by: Artem Glazychev <artem.glazychev@xored.com>
  • Loading branch information
glazychev-art authored Jul 20, 2022
1 parent dc978c4 commit 7349b1f
Show file tree
Hide file tree
Showing 8 changed files with 353 additions and 29 deletions.
4 changes: 2 additions & 2 deletions pkg/networkservice/chains/nsmgr/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import (
"github.com/networkservicemesh/sdk/pkg/networkservice/common/connect"
"github.com/networkservicemesh/sdk/pkg/networkservice/common/mechanisms/kernel"
"github.com/networkservicemesh/sdk/pkg/networkservice/common/mechanismtranslation"
"github.com/networkservicemesh/sdk/pkg/networkservice/common/replacelabels"
"github.com/networkservicemesh/sdk/pkg/networkservice/common/passthrough"
"github.com/networkservicemesh/sdk/pkg/networkservice/core/chain"
"github.com/networkservicemesh/sdk/pkg/networkservice/utils/count"
"github.com/networkservicemesh/sdk/pkg/networkservice/utils/inject/injecterror"
Expand Down Expand Up @@ -794,7 +794,7 @@ func additionalFunctionalityChain(ctx context.Context, clientURL *url.URL, clien
client.WithName(fmt.Sprintf("endpoint-client-%v", clientName)),
client.WithAdditionalFunctionality(
mechanismtranslation.NewClient(),
replacelabels.NewClient(labels),
passthrough.NewClient(labels),
kernel.NewClient(),
),
client.WithDialOptions(sandbox.DialOptions()...),
Expand Down
34 changes: 34 additions & 0 deletions pkg/networkservice/common/passthrough/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) 2022 Cisco 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 passthrough combines chain elements used in passthrough cases
package passthrough

import (
"github.com/networkservicemesh/api/pkg/api/networkservice"

"github.com/networkservicemesh/sdk/pkg/networkservice/common/passthrough/replacelabels"
"github.com/networkservicemesh/sdk/pkg/networkservice/common/passthrough/replacensename"
"github.com/networkservicemesh/sdk/pkg/networkservice/core/chain"
)

// NewClient returns a combination for passthrough cases
func NewClient(labels map[string]string) networkservice.NetworkServiceClient {
return chain.NewNetworkServiceClient(
replacelabels.NewClient(labels),
replacensename.NewClient(),
)
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Copyright (c) 2021 Doc.ai and/or its affiliates.
//
// Copyright (c) 2022 Cisco and/or its affiliates.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -29,45 +31,30 @@ import (
)

type replaceLabelsClient struct {
labels map[string]string
oldConnLabels map[string]string
oldNseName string
labels map[string]string
}

func (s *replaceLabelsClient) Request(ctx context.Context, request *networkservice.NetworkServiceRequest, opts ...grpc.CallOption) (conn *networkservice.Connection, err error) {
s.oldConnLabels = request.Connection.Labels
s.oldNseName = request.Connection.NetworkServiceEndpointName
// NewClient creates new instance of NetworkServiceClient chain element, which replaces labels in the connection
func NewClient(labels map[string]string) networkservice.NetworkServiceClient {
return &replaceLabelsClient{
labels: labels,
}
}

func (s *replaceLabelsClient) Request(ctx context.Context, request *networkservice.NetworkServiceRequest, opts ...grpc.CallOption) (conn *networkservice.Connection, err error) {
prevConnLabels := request.Connection.Labels
request.Connection.Labels = s.labels
request.Connection.NetworkServiceEndpointName = ""

conn, err = next.Client(ctx).Request(ctx, request, opts...)
if err != nil {
return nil, err
}

conn.Labels = s.oldConnLabels
conn.NetworkServiceEndpointName = s.oldNseName
conn.Labels = prevConnLabels

return conn, nil
}

func (s *replaceLabelsClient) Close(ctx context.Context, conn *networkservice.Connection, opts ...grpc.CallOption) (*empty.Empty, error) {
s.oldConnLabels = conn.Labels
s.oldNseName = conn.NetworkServiceEndpointName

rv, err := next.Client(ctx).Close(ctx, conn, opts...)

conn.Labels = s.oldConnLabels
conn.NetworkServiceEndpointName = s.oldNseName

return rv, err
}

// NewClient creates new instance of NetworkServiceClient chain element, which replaces labels in the connection
func NewClient(labels map[string]string) networkservice.NetworkServiceClient {
return &replaceLabelsClient{
labels: labels,
oldConnLabels: make(map[string]string),
}
conn.Labels = s.labels
return next.Client(ctx).Close(ctx, conn, opts...)
}
61 changes: 61 additions & 0 deletions pkg/networkservice/common/passthrough/replacelabels/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright (c) 2022 Cisco 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 replacelabels_test

import (
"context"
"testing"

"go.uber.org/goleak"

"github.com/stretchr/testify/require"

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

"github.com/networkservicemesh/sdk/pkg/networkservice/common/passthrough/replacelabels"
"github.com/networkservicemesh/sdk/pkg/networkservice/core/chain"
"github.com/networkservicemesh/sdk/pkg/networkservice/utils/checks/checkclose"
"github.com/networkservicemesh/sdk/pkg/networkservice/utils/checks/checkrequest"
"github.com/networkservicemesh/sdk/pkg/networkservice/utils/metadata"
)

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

chainLabels := map[string]string{"1": "A", "2": "B"}
client := chain.NewNetworkServiceClient(
metadata.NewClient(),
replacelabels.NewClient(chainLabels),
checkrequest.NewClient(t, func(t *testing.T, r *networkservice.NetworkServiceRequest) {
require.Equal(t, chainLabels, r.Connection.Labels)
}),
checkclose.NewClient(t, func(t *testing.T, c *networkservice.Connection) {
require.Equal(t, chainLabels, c.Labels)
}),
)

// Create the request with any labels
req := &networkservice.NetworkServiceRequest{
Connection: &networkservice.Connection{Id: "nsc-1", Labels: map[string]string{"3": "C"}},
}
conn, err := client.Request(context.Background(), req)
require.NoError(t, err)
require.Equal(t, map[string]string{"3": "C"}, conn.Labels)

_, err = client.Close(context.Background(), conn)
require.NoError(t, err)
}
56 changes: 56 additions & 0 deletions pkg/networkservice/common/passthrough/replacensename/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright (c) 2022 Cisco 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 replacensename replaces NetworkServiceEndpointName if it was discovered before
package replacensename

import (
"context"

"github.com/golang/protobuf/ptypes/empty"
"google.golang.org/grpc"

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

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

type replaceNSEClient struct{}

// NewClient creates new instance of NetworkServiceClient chain element, which replaces NetworkServiceEndpointName in the connection
func NewClient() networkservice.NetworkServiceClient {
return &replaceNSEClient{}
}

func (s *replaceNSEClient) Request(ctx context.Context, request *networkservice.NetworkServiceRequest, opts ...grpc.CallOption) (conn *networkservice.Connection, err error) {
prevNseName := request.Connection.NetworkServiceEndpointName
request.Connection.NetworkServiceEndpointName, _ = load(ctx)

conn, err = next.Client(ctx).Request(ctx, request, opts...)
if err != nil {
return nil, err
}

store(ctx, conn.NetworkServiceEndpointName)
conn.NetworkServiceEndpointName = prevNseName

return conn, nil
}

func (s *replaceNSEClient) Close(ctx context.Context, conn *networkservice.Connection, opts ...grpc.CallOption) (*empty.Empty, error) {
conn.NetworkServiceEndpointName, _ = loadAndDelete(ctx)
return next.Client(ctx).Close(ctx, conn, opts...)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright (c) 2022 Cisco 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 replacensename_test

import (
"context"
"testing"

"go.uber.org/goleak"
"google.golang.org/grpc"

"github.com/golang/protobuf/ptypes/empty"
"github.com/stretchr/testify/require"

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

"github.com/networkservicemesh/sdk/pkg/networkservice/common/passthrough/replacensename"
"github.com/networkservicemesh/sdk/pkg/networkservice/core/chain"
"github.com/networkservicemesh/sdk/pkg/networkservice/core/next"
"github.com/networkservicemesh/sdk/pkg/networkservice/utils/checks/checkclose"
"github.com/networkservicemesh/sdk/pkg/networkservice/utils/checks/checkrequest"
"github.com/networkservicemesh/sdk/pkg/networkservice/utils/metadata"
)

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

client := chain.NewNetworkServiceClient(
metadata.NewClient(),
replacensename.NewClient(),
&setNSENameClient{name: "nse-name1"},
checkrequest.NewClient(t, func(t *testing.T, r *networkservice.NetworkServiceRequest) {
require.Equal(t, "nse-name1", r.Connection.NetworkServiceEndpointName)
}),
checkclose.NewClient(t, func(t *testing.T, c *networkservice.Connection) {
require.Equal(t, "nse-name1", c.NetworkServiceEndpointName)
}),
)
req := &networkservice.NetworkServiceRequest{
Connection: &networkservice.Connection{Id: "nsc-1"},
}
conn, err := client.Request(context.Background(), req)
require.NoError(t, err)

// Change NetworkServiceEndpointName to another name
conn.NetworkServiceEndpointName = "nse-name2"
conn, err = client.Request(context.Background(), req)
require.Equal(t, "nse-name2", conn.NetworkServiceEndpointName)
require.NoError(t, err)

_, err = client.Close(context.Background(), conn)
require.NoError(t, err)
}

// setNSENameClient sets NetworkServiceEndpointName only if it is empty
type setNSENameClient struct {
name string
}

func (s *setNSENameClient) Request(ctx context.Context, request *networkservice.NetworkServiceRequest, opts ...grpc.CallOption) (*networkservice.Connection, error) {
if request.GetConnection().NetworkServiceEndpointName == "" {
request.GetConnection().NetworkServiceEndpointName = s.name
}
return next.Client(ctx).Request(ctx, request, opts...)
}

func (s *setNSENameClient) Close(ctx context.Context, conn *networkservice.Connection, opts ...grpc.CallOption) (*empty.Empty, error) {
return next.Client(ctx).Close(ctx, conn, opts...)
}
50 changes: 50 additions & 0 deletions pkg/networkservice/common/passthrough/replacensename/metadata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (c) 2022 Cisco 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 replacensename

import (
"context"

"github.com/networkservicemesh/sdk/pkg/networkservice/utils/metadata"
)

type keyType struct{}

// store - stores the next network service endpoint name
func store(ctx context.Context, nseName string) {
metadata.Map(ctx, true).Store(keyType{}, nseName)
}

// load - returns the next network service endpoint name
func load(ctx context.Context) (value string, ok bool) {
rawValue, ok := metadata.Map(ctx, true).Load(keyType{})
if !ok {
return
}
value, ok = rawValue.(string)
return value, ok
}

// loadAndDelete - returns the next network service endpoint name and deletes it from the metadata
func loadAndDelete(ctx context.Context) (value string, ok bool) {
rawValue, ok := metadata.Map(ctx, true).LoadAndDelete(keyType{})
if !ok {
return
}
value, ok = rawValue.(string)
return value, ok
}
Loading

0 comments on commit 7349b1f

Please sign in to comment.