Skip to content

Commit

Permalink
Add switch-case server, client chain elements
Browse files Browse the repository at this point in the history
Signed-off-by: Vladimir Popov <vladimir.popov@xored.com>
  • Loading branch information
Vladimir Popov committed Jul 29, 2021
1 parent 92b2824 commit 170ca4a
Show file tree
Hide file tree
Showing 8 changed files with 403 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -279,3 +279,6 @@ issues:
- path: pkg/networkservice/chains/nsmgrproxy/server_test.go
linters:
- funlen
- path: pkg/networkservice/common/swich/.*_test.go
linters:
- dupl
62 changes: 62 additions & 0 deletions pkg/networkservice/common/swich/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// 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 swich

import (
"context"

"github.com/pkg/errors"
"google.golang.org/grpc"
"google.golang.org/protobuf/types/known/emptypb"

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

// ClientCase is a case type for the switch-case client chain element
type ClientCase struct {
Condition Condition
Client networkservice.NetworkServiceClient
}

type switchClient struct {
cases []*ClientCase
}

// NewClient returns a new switch-case client chain element
func NewClient(cases ...*ClientCase) networkservice.NetworkServiceClient {
return &switchClient{
cases: cases,
}
}

func (s *switchClient) Request(ctx context.Context, request *networkservice.NetworkServiceRequest, opts ...grpc.CallOption) (*networkservice.Connection, error) {
for _, c := range s.cases {
if c.Condition(ctx, request.GetConnection()) {
return c.Client.Request(ctx, request, opts...)
}
}
return nil, errors.WithStack(errors.New("all cases failed"))
}

func (s *switchClient) Close(ctx context.Context, conn *networkservice.Connection, opts ...grpc.CallOption) (*emptypb.Empty, error) {
for _, c := range s.cases {
if c.Condition(ctx, conn) {
return c.Client.Close(ctx, conn, opts...)
}
}
return nil, errors.WithStack(errors.New("all cases failed"))
}
76 changes: 76 additions & 0 deletions pkg/networkservice/common/swich/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// 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 swich_test

import (
"context"
"testing"

"github.com/stretchr/testify/require"

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

"github.com/networkservicemesh/sdk/pkg/networkservice/common/swich"
"github.com/networkservicemesh/sdk/pkg/networkservice/utils/checks/checkcontext"
)

func TestSwitchClient(t *testing.T) {
for _, s := range testSamples() {
t.Run(s.name, func(t *testing.T) {
// nolint:scopelint
testSwitchClient(t, s.conditions, s.result)
})
}
}

func testSwitchClient(t *testing.T, conditions []swich.Condition, expected int) {
var actual int

var cases []*swich.ClientCase
for i, cond := range conditions {
i := i
cases = append(cases, &swich.ClientCase{
Condition: cond,
Client: checkcontext.NewClient(t, func(*testing.T, context.Context) {
actual = i
}),
})
}

s := swich.NewClient(cases...)
ctx := withN(context.Background(), 1)

actual = -1
_, err := s.Request(ctx, new(networkservice.NetworkServiceRequest))
require.Equal(t, expected, actual)

if expected != -1 {
require.NoError(t, err)
} else {
require.Error(t, err)
}

actual = -1
_, err = s.Close(ctx, new(networkservice.Connection))
require.Equal(t, expected, actual)

if expected != -1 {
require.NoError(t, err)
} else {
require.Error(t, err)
}
}
75 changes: 75 additions & 0 deletions pkg/networkservice/common/swich/common_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// 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 swich_test

import (
"context"
"fmt"

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

"github.com/networkservicemesh/sdk/pkg/networkservice/common/swich"
)

type sample struct {
name string
conditions []swich.Condition
result int
}

func testSamples() (samples []*sample) {
for i := 0; i < 8; i++ {
s := &sample{
conditions: make([]swich.Condition, 3),
result: -1,
}

bits := i
for k := range s.conditions {
bit := bits % 2
s.conditions[k] = condition(bit)
if bit == 1 {
s.name = fmt.Sprint(s.name, "true ")
if s.result == -1 {
s.result = k
}
} else {
s.name = fmt.Sprint(s.name, "false ")
}
bits /= 2
}
s.name = fmt.Sprint(s.name, s.result)

samples = append(samples, s)
}
return samples
}

func condition(n int) swich.Condition {
return func(ctx context.Context, _ *networkservice.Connection) bool {
if value := ctx.Value("key"); value != nil {
num, ok := value.(int)
return ok && num == n
}
return false
}
}

func withN(ctx context.Context, n int) context.Context {
// nolint:golint,staticcheck
return context.WithValue(ctx, "key", n)
}
31 changes: 31 additions & 0 deletions pkg/networkservice/common/swich/default.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// 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 swich

import (
"context"

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

// Condition is a type for swich.*Case condition
type Condition = func(context.Context, *networkservice.Connection) bool

// Default is a "default" case condition for swich
func Default(context.Context, *networkservice.Connection) bool {
return true
}
19 changes: 19 additions & 0 deletions pkg/networkservice/common/swich/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// 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 swich provides chain elements acting like a switch-case statement, selecting a chain element with first
// succeed condition
package swich
61 changes: 61 additions & 0 deletions pkg/networkservice/common/swich/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// 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 swich

import (
"context"

"github.com/pkg/errors"
"google.golang.org/protobuf/types/known/emptypb"

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

// ServerCase is a case type for the switch-case server chain element
type ServerCase struct {
Condition Condition
Server networkservice.NetworkServiceServer
}

type switchServer struct {
cases []*ServerCase
}

// NewServer returns a new switch-case server chain element
func NewServer(cases ...*ServerCase) networkservice.NetworkServiceServer {
return &switchServer{
cases: cases,
}
}

func (s *switchServer) Request(ctx context.Context, request *networkservice.NetworkServiceRequest) (*networkservice.Connection, error) {
for _, c := range s.cases {
if c.Condition(ctx, request.GetConnection()) {
return c.Server.Request(ctx, request)
}
}
return nil, errors.WithStack(errors.New("all cases failed"))
}

func (s *switchServer) Close(ctx context.Context, conn *networkservice.Connection) (*emptypb.Empty, error) {
for _, c := range s.cases {
if c.Condition(ctx, conn) {
return c.Server.Close(ctx, conn)
}
}
return nil, errors.WithStack(errors.New("all cases failed"))
}
Loading

0 comments on commit 170ca4a

Please sign in to comment.