From 7349b1fc45f40a982a5126d0f62a8c28b9510ac2 Mon Sep 17 00:00:00 2001 From: Artem Glazychev Date: Wed, 20 Jul 2022 16:29:51 +0700 Subject: [PATCH] Add replaceNSEName chain element (#1328) * Add replaceNSEName chain element Signed-off-by: Artem Glazychev * Create passthrough chain element Signed-off-by: Artem Glazychev --- pkg/networkservice/chains/nsmgr/suite_test.go | 4 +- .../common/passthrough/client.go | 34 ++++++++ .../{ => passthrough}/replacelabels/client.go | 41 ++++----- .../passthrough/replacelabels/client_test.go | 61 ++++++++++++++ .../passthrough/replacensename/client.go | 56 +++++++++++++ .../passthrough/replacensename/client_test.go | 83 +++++++++++++++++++ .../passthrough/replacensename/metadata.go | 50 +++++++++++ .../utils/checks/checkclose/client.go | 53 ++++++++++++ 8 files changed, 353 insertions(+), 29 deletions(-) create mode 100644 pkg/networkservice/common/passthrough/client.go rename pkg/networkservice/common/{ => passthrough}/replacelabels/client.go (72%) create mode 100644 pkg/networkservice/common/passthrough/replacelabels/client_test.go create mode 100644 pkg/networkservice/common/passthrough/replacensename/client.go create mode 100644 pkg/networkservice/common/passthrough/replacensename/client_test.go create mode 100644 pkg/networkservice/common/passthrough/replacensename/metadata.go create mode 100644 pkg/networkservice/utils/checks/checkclose/client.go diff --git a/pkg/networkservice/chains/nsmgr/suite_test.go b/pkg/networkservice/chains/nsmgr/suite_test.go index e484f1fdd..294a06e5a 100644 --- a/pkg/networkservice/chains/nsmgr/suite_test.go +++ b/pkg/networkservice/chains/nsmgr/suite_test.go @@ -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" @@ -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()...), diff --git a/pkg/networkservice/common/passthrough/client.go b/pkg/networkservice/common/passthrough/client.go new file mode 100644 index 000000000..a4e47b150 --- /dev/null +++ b/pkg/networkservice/common/passthrough/client.go @@ -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(), + ) +} diff --git a/pkg/networkservice/common/replacelabels/client.go b/pkg/networkservice/common/passthrough/replacelabels/client.go similarity index 72% rename from pkg/networkservice/common/replacelabels/client.go rename to pkg/networkservice/common/passthrough/replacelabels/client.go index 14a944978..f6c60badb 100644 --- a/pkg/networkservice/common/replacelabels/client.go +++ b/pkg/networkservice/common/passthrough/replacelabels/client.go @@ -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"); @@ -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...) } diff --git a/pkg/networkservice/common/passthrough/replacelabels/client_test.go b/pkg/networkservice/common/passthrough/replacelabels/client_test.go new file mode 100644 index 000000000..945275692 --- /dev/null +++ b/pkg/networkservice/common/passthrough/replacelabels/client_test.go @@ -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) +} diff --git a/pkg/networkservice/common/passthrough/replacensename/client.go b/pkg/networkservice/common/passthrough/replacensename/client.go new file mode 100644 index 000000000..62ff9cbaf --- /dev/null +++ b/pkg/networkservice/common/passthrough/replacensename/client.go @@ -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...) +} diff --git a/pkg/networkservice/common/passthrough/replacensename/client_test.go b/pkg/networkservice/common/passthrough/replacensename/client_test.go new file mode 100644 index 000000000..2f1841a14 --- /dev/null +++ b/pkg/networkservice/common/passthrough/replacensename/client_test.go @@ -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...) +} diff --git a/pkg/networkservice/common/passthrough/replacensename/metadata.go b/pkg/networkservice/common/passthrough/replacensename/metadata.go new file mode 100644 index 000000000..80931a536 --- /dev/null +++ b/pkg/networkservice/common/passthrough/replacensename/metadata.go @@ -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 +} diff --git a/pkg/networkservice/utils/checks/checkclose/client.go b/pkg/networkservice/utils/checks/checkclose/client.go new file mode 100644 index 000000000..354aeefb2 --- /dev/null +++ b/pkg/networkservice/utils/checks/checkclose/client.go @@ -0,0 +1,53 @@ +// 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 checkclose - provides networkservice chain elements to check the Close received from the previous element in the chain +package checkclose + +import ( + "context" + "testing" + + "github.com/golang/protobuf/ptypes/empty" + "github.com/networkservicemesh/api/pkg/api/networkservice" + "google.golang.org/grpc" + + "github.com/networkservicemesh/sdk/pkg/networkservice/core/next" +) + +type checkCloseClient struct { + *testing.T + check func(*testing.T, *networkservice.Connection) +} + +// NewClient - returns NetworkServiceClient chain elements to check the Close received from the previous element in the chain +// t - *testing.T for checks +// check - function to check the Connection +func NewClient(t *testing.T, check func(*testing.T, *networkservice.Connection)) networkservice.NetworkServiceClient { + return &checkCloseClient{ + T: t, + check: check, + } +} + +func (c *checkCloseClient) Request(ctx context.Context, request *networkservice.NetworkServiceRequest, opts ...grpc.CallOption) (*networkservice.Connection, error) { + return next.Client(ctx).Request(ctx, request, opts...) +} + +func (c *checkCloseClient) Close(ctx context.Context, conn *networkservice.Connection, opts ...grpc.CallOption) (*empty.Empty, error) { + c.check(c.T, conn) + return next.Client(ctx).Close(ctx, conn, opts...) +}