Skip to content

Commit

Permalink
Add network service to MonitorScopeSelector (networkservicemesh#178)
Browse files Browse the repository at this point in the history
Allow MonitorScopeSelector to filter connections based
on network service as well.

Signed-off-by: Lugossy Zoltan <zoltan.lugossy@est.tech>
  • Loading branch information
zolug committed Dec 4, 2024
1 parent df76555 commit e2e147b
Show file tree
Hide file tree
Showing 4 changed files with 263 additions and 38 deletions.
85 changes: 57 additions & 28 deletions pkg/api/networkservice/connection.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pkg/api/networkservice/connection.proto
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright 2018 Red Hat, Inc.
// Copyright (c) 2018 Cisco and/or its affiliates.
// Copyright (c) 2024 Nordix Foundation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -91,6 +92,7 @@ message ConnectionEvent {

message MonitorScopeSelector {
repeated PathSegment path_segments = 1;
repeated string network_services = 2;
}

service MonitorConnection {
Expand Down
55 changes: 45 additions & 10 deletions pkg/api/networkservice/connection_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
//
// Copyright (c) 2023-2024 Cisco and/or its affiliates.
//
// Copyright (c) 2024 Nordix Foundation.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -37,21 +39,36 @@ func (x *Connection) Clone() *Connection {
return proto.Clone(x).(*Connection)
}

// MatchesMonitorScopeSelector - Returns true of the connection matches the selector
func (x *Connection) MatchesMonitorScopeSelector(selector *MonitorScopeSelector) bool {
if x == nil {
return false
// Checks if the connection matches any of the network services in the given
// network service selector.
// Treats an empty NetworkServices in the selector as a wildcard.
func (x *Connection) matchesNetworkServices(selector *MonitorScopeSelector) bool {
selectorServices := selector.GetNetworkServices()
if len(selectorServices) == 0 {
return true // Wildcard match
}
// Note: Empty selector always matches a non-nil Connection

for _, service := range selectorServices {
if service == x.GetNetworkService() {
return true // Match found
}
}

return false
}

// Iterates through the connection's PathSegments array looking for a subarray
// that matches the selector's PathSegments array.
// Treats an empty PathSegments in the selector or empty strings in the selector
// as wildcards.
func (x *Connection) matchesPathSegments(selector *MonitorScopeSelector) bool {
if len(selector.GetPathSegments()) == 0 {
return true
return true // Wildcard match
}
// Iterate through the Connection.PathSegments array looking for a subarray that matches
// the selector.PathSegments array, treating "" in the selector.PathSegments array
// as a wildcard

for i := range x.GetPath().GetPathSegments() {
// If there aren't enough elements left in the Connection.PathSegments array to match
// all of the elements in the select.PathSegments array...clearly we can't match
// all of the elements in the selector.PathSegments array...clearly we can't match
if i+len(selector.GetPathSegments()) > len(x.GetPath().GetPathSegments()) {
return false
}
Expand Down Expand Up @@ -79,9 +96,27 @@ func (x *Connection) MatchesMonitorScopeSelector(selector *MonitorScopeSelector)
}
}
}

return false
}

// MatchesMonitorScopeSelector - Returns true of the connection matches the selector
func (x *Connection) MatchesMonitorScopeSelector(selector *MonitorScopeSelector) bool {
if x == nil {
return false
}
// Empty selector matches any non-nil connection
if len(selector.GetPathSegments()) == 0 && len(selector.GetNetworkServices()) == 0 {
return true
}
// Check network service match first
if !x.matchesNetworkServices(selector) {
return false
}
// Check path segments match
return x.matchesPathSegments(selector)
}

// GetCurrentPathSegment - Get the current path segment of the connection
func (x *Connection) GetCurrentPathSegment() *PathSegment {
if x == nil {
Expand Down
159 changes: 159 additions & 0 deletions pkg/api/networkservice/connection_helpers_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Copyright (c) 2023 Cisco and/or its affiliates.
//
// Copyright (c) 2024 Nordix Foundation.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -99,3 +101,160 @@ func TestMonitorScopeSelector(t *testing.T) {
})
}
}

// nolint: funlen
func TestNetworkServiceMonitorScopeSelector(t *testing.T) {
cases := []struct {
testname string
conn *networkservice.Connection
selector *networkservice.MonitorScopeSelector
matches bool
}{
{
testname: "EmptySelector",
conn: &networkservice.Connection{
Path: &networkservice.Path{
PathSegments: []*networkservice.PathSegment{{Name: "s1", Id: "id1", Token: "t1"}, {Name: "s2", Id: "id2", Token: "t2"}},
},
NetworkService: "ns1",
},
selector: &networkservice.MonitorScopeSelector{},
matches: true,
},
{
testname: "IdenticalPathsAndNetworkService",
conn: &networkservice.Connection{
Path: &networkservice.Path{
PathSegments: []*networkservice.PathSegment{{Name: "s1", Id: "id1", Token: "t1"}, {Name: "s2", Id: "id2", Token: "t2"}},
},
NetworkService: "ns1",
},
selector: &networkservice.MonitorScopeSelector{
PathSegments: []*networkservice.PathSegment{{Name: "s1", Id: "id1", Token: "t1"}, {Name: "s2", Id: "id2", Token: "t2"}},
NetworkServices: []string{"ns1"},
},
matches: true,
},
{
testname: "IdenticalPathsAndDifferentNetworkService",
conn: &networkservice.Connection{
Path: &networkservice.Path{
PathSegments: []*networkservice.PathSegment{{Name: "s1", Id: "id1", Token: "t1"}, {Name: "s2", Id: "id2", Token: "t2"}},
},
NetworkService: "ns1",
},
selector: &networkservice.MonitorScopeSelector{
PathSegments: []*networkservice.PathSegment{{Name: "s1", Id: "id1", Token: "t1"}, {Name: "s2", Id: "id2", Token: "t2"}},
NetworkServices: []string{"ns2"},
},
matches: false,
},
{
testname: "IdenticalPathsAndMatchingNetworkServiceList",
conn: &networkservice.Connection{
Path: &networkservice.Path{
PathSegments: []*networkservice.PathSegment{{Name: "s1", Id: "id1", Token: "t1"}, {Name: "s2", Id: "id2", Token: "t2"}},
},
NetworkService: "ns1",
},
selector: &networkservice.MonitorScopeSelector{
PathSegments: []*networkservice.PathSegment{{Name: "s1", Id: "id1", Token: "t1"}, {Name: "s2", Id: "id2", Token: "t2"}},
NetworkServices: []string{"ns2", "ns1", "ns3"},
},
matches: true,
},
{
testname: "IdenticalPathsAndNonMatchingNetworkServiceList",
conn: &networkservice.Connection{
Path: &networkservice.Path{
PathSegments: []*networkservice.PathSegment{{Name: "s1", Id: "id1", Token: "t1"}, {Name: "s2", Id: "id2", Token: "t2"}},
},
NetworkService: "ns1",
},
selector: &networkservice.MonitorScopeSelector{
PathSegments: []*networkservice.PathSegment{{Name: "s1", Id: "id1", Token: "t1"}, {Name: "s2", Id: "id2", Token: "t2"}},
NetworkServices: []string{"ns2", "ns3", "ns0"},
},
matches: false,
},
{
testname: "IdenticalNetworkService",
conn: &networkservice.Connection{
Path: &networkservice.Path{
PathSegments: []*networkservice.PathSegment{{Name: "s1", Id: "id1", Token: "t1"}, {Name: "s2", Id: "id2", Token: "t2"}},
},
NetworkService: "ns1",
},
selector: &networkservice.MonitorScopeSelector{
NetworkServices: []string{"ns1"},
},
matches: true,
},
{
testname: "IdenticalNetworkServiceEmptyConnectionPath",
conn: &networkservice.Connection{
NetworkService: "ns1",
},
selector: &networkservice.MonitorScopeSelector{
NetworkServices: []string{"ns1"},
},
matches: true,
},
{
testname: "DifferentNetworkService",
conn: &networkservice.Connection{
Path: &networkservice.Path{
PathSegments: []*networkservice.PathSegment{{Name: "s1", Id: "id1", Token: "t1"}, {Name: "s2", Id: "id2", Token: "t2"}},
},
NetworkService: "ns1",
},
selector: &networkservice.MonitorScopeSelector{
NetworkServices: []string{"ns2"},
},
matches: false,
},
{
testname: "DifferentNetworkServiceEmptyConnectionPath",
conn: &networkservice.Connection{
NetworkService: "ns1",
},
selector: &networkservice.MonitorScopeSelector{
NetworkServices: []string{"ns2"},
},
matches: false,
},
{
testname: "MatchingNetworkServiceList",
conn: &networkservice.Connection{
Path: &networkservice.Path{
PathSegments: []*networkservice.PathSegment{{Name: "s1", Id: "id1", Token: "t1"}, {Name: "s2", Id: "id2", Token: "t2"}},
},
NetworkService: "ns1",
},
selector: &networkservice.MonitorScopeSelector{
NetworkServices: []string{"ns2", "ns1", "ns3"},
},
matches: true,
},
{
testname: "NonMatchingNetworkServiceList",
conn: &networkservice.Connection{
Path: &networkservice.Path{
PathSegments: []*networkservice.PathSegment{{Name: "s1", Id: "id1", Token: "t1"}, {Name: "s2", Id: "id2", Token: "t2"}},
},
NetworkService: "ns1",
},
selector: &networkservice.MonitorScopeSelector{
NetworkServices: []string{"ns2", "ns3", "ns0"},
},
matches: false,
},
}

for _, testCase := range cases {
c := testCase
t.Run(c.testname, func(t *testing.T) {
require.Equal(t, c.conn.MatchesMonitorScopeSelector(c.selector), c.matches)
})
}
}

0 comments on commit e2e147b

Please sign in to comment.