Skip to content

Commit

Permalink
Add util function PathMatchesQuery (#601)
Browse files Browse the repository at this point in the history
* Add util function PathMatchesQuery
  • Loading branch information
DanG100 authored Nov 9, 2021
1 parent 15063cb commit ef8f2a1
Show file tree
Hide file tree
Showing 2 changed files with 226 additions and 1 deletion.
33 changes: 32 additions & 1 deletion util/gnmi.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ func PathMatchesPrefix(path *gpb.Path, prefix []string) bool {
}

// PathElemsEqual replaces the proto.Equal() check for PathElems.
// If a.Key["foo"] == "*" and b.Key["foo"] == "bar" func returns false.
// This significantly improves comparison speed.
func PathElemsEqual(a, b *gpb.PathElem) bool {
// This check allows avoiding to deal with any null PathElems later on.
Expand All @@ -53,12 +54,13 @@ func PathElemsEqual(a, b *gpb.PathElem) bool {
if a.Name != b.Name {
return false
}

if len(a.Key) != len(b.Key) {
return false
}

for k, v := range a.Key {
if vo, ok := b.Key[k]; !ok || vo != v {
if vo, ok := b.Key[k]; !ok || v != vo {
return false
}
}
Expand All @@ -80,6 +82,9 @@ func PathElemSlicesEqual(a, b []*gpb.PathElem) bool {

// PathMatchesPathElemPrefix checks whether prefix is a prefix of path. Both paths
// must use the gNMI >=0.4.0 PathElem path format.
// Note: Paths must match exactly, that is if path has a wildcard key,
// then the same key must also be a wildcard in the prefix.
// See PathMatchesQuery for comparing paths with wildcards.
func PathMatchesPathElemPrefix(path, prefix *gpb.Path) bool {
if len(path.GetElem()) < len(prefix.GetElem()) || path.Origin != prefix.Origin {
return false
Expand All @@ -92,6 +97,32 @@ func PathMatchesPathElemPrefix(path, prefix *gpb.Path) bool {
return true
}

// PathMatchesQuery returns whether query is prefix of path.
// Only the query may contain wildcard name or keys.
// TODO: Multilevel wildcards ("...") not supported.
// If either path and query contain nil elements func returns false.
// Both paths must use the gNMI >=0.4.0 PathElem path format.
func PathMatchesQuery(path, query *gpb.Path) bool {
if len(path.GetElem()) < len(query.GetElem()) || path.Origin != query.Origin {
return false
}
for i, queryElem := range query.Elem {
pathElem := path.Elem[i]
if queryElem == nil || pathElem == nil {
return false
}
if queryElem.Name != "*" && queryElem.Name != pathElem.Name {
return false
}
for qk, qv := range queryElem.Key {
if pv, ok := pathElem.Key[qk]; !ok || (qv != "*" && qv != pv) {
return false
}
}
}
return true
}

// TrimGNMIPathPrefix returns path with the prefix trimmed. It returns the
// original path if the prefix does not fully match.
func TrimGNMIPathPrefix(path *gpb.Path, prefix []string) *gpb.Path {
Expand Down
194 changes: 194 additions & 0 deletions util/gnmi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,200 @@ func TestPathMatchesPathElemPrefix(t *testing.T) {
}
}

func TestPathMatchesQuery(t *testing.T) {
tests := []struct {
desc string
inPath *gpb.Path
inQuery *gpb.Path
want bool
}{{
desc: "valid query with no keys",
inPath: &gpb.Path{
Elem: []*gpb.PathElem{{
Name: "one",
}, {
Name: "two",
}},
},
inQuery: &gpb.Path{
Elem: []*gpb.PathElem{{
Name: "one",
}},
},
want: true,
}, {
desc: "valid query with wildcard name",
inPath: &gpb.Path{
Elem: []*gpb.PathElem{{
Name: "one",
}, {
Name: "two",
}},
},
inQuery: &gpb.Path{
Elem: []*gpb.PathElem{{
Name: "*",
}, {
Name: "two",
}},
},
want: true,
}, {
desc: "valid query with exact key match",
inPath: &gpb.Path{
Elem: []*gpb.PathElem{{
Name: "one",
Key: map[string]string{"two": "three"},
}, {
Name: "four",
}},
},
inQuery: &gpb.Path{
Elem: []*gpb.PathElem{{
Name: "one",
Key: map[string]string{"two": "three"},
}},
},
want: true,
}, {
desc: "valid query with wildcard keys",
inPath: &gpb.Path{
Elem: []*gpb.PathElem{{
Name: "one",
Key: map[string]string{"two": "three"},
}, {
Name: "four",
}},
},
inQuery: &gpb.Path{
Elem: []*gpb.PathElem{{
Name: "one",
Key: map[string]string{"two": "*"},
}},
},
want: true,
}, {
desc: "valid query with no keys and path with keys",
inPath: &gpb.Path{
Elem: []*gpb.PathElem{{
Name: "one",
Key: map[string]string{"two": "three"},
}, {
Name: "four",
}},
},
inQuery: &gpb.Path{
Elem: []*gpb.PathElem{{
Name: "one",
}},
},
want: true,
}, {
desc: "valid query with both missing and wildcard keys",
inPath: &gpb.Path{
Elem: []*gpb.PathElem{{
Name: "one",
Key: map[string]string{
"two": "three",
"four": "five",
},
}, {
Name: "four",
}},
},
inQuery: &gpb.Path{
Elem: []*gpb.PathElem{{
Name: "one",
Key: map[string]string{"four": "*"},
}},
},
want: true,
}, {
desc: "invalid nil elements",
inPath: &gpb.Path{
Elem: []*gpb.PathElem{
nil,
{
Name: "twelve",
}},
},
inQuery: &gpb.Path{
Elem: []*gpb.PathElem{{
Name: "three",
}},
},
}, {
desc: "invalid names not equal",
inPath: &gpb.Path{
Elem: []*gpb.PathElem{{
Name: "fourteen",
}, {
Name: "twelve",
}},
},
inQuery: &gpb.Path{
Elem: []*gpb.PathElem{{
Name: "three",
}},
},
}, {
desc: "invalid origin",
inPath: &gpb.Path{
Origin: "openconfig",
Elem: []*gpb.PathElem{{
Name: "one",
}, {
Name: "two",
}},
},
inQuery: &gpb.Path{
Origin: "google",
Elem: []*gpb.PathElem{{
Name: "one",
}},
},
}, {
desc: "invalid keys",
inPath: &gpb.Path{
Elem: []*gpb.PathElem{{
Name: "three",
Key: map[string]string{"four": "five"},
}, {
Name: "six",
}},
},
inQuery: &gpb.Path{
Elem: []*gpb.PathElem{{
Name: "three",
Key: map[string]string{"seven": "eight"},
}},
},
}, {
desc: "invalid missing wildcard keys",
inPath: &gpb.Path{
Elem: []*gpb.PathElem{{
Name: "three",
Key: map[string]string{"four": "five"},
}, {
Name: "six",
}},
},
inQuery: &gpb.Path{
Elem: []*gpb.PathElem{{
Name: "three",
Key: map[string]string{"seven": "*"},
}},
},
}}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
if got := PathMatchesQuery(tt.inPath, tt.inQuery); got != tt.want {
t.Fatalf("did not get expected result, got: %v, want: %v", got, tt.want)
}
})
}
}

func TestTrimGNMIPathElemPrefix(t *testing.T) {
tests := []struct {
desc string
Expand Down

0 comments on commit ef8f2a1

Please sign in to comment.