Skip to content

Commit ef8f2a1

Browse files
authored
Add util function PathMatchesQuery (#601)
* Add util function PathMatchesQuery
1 parent 15063cb commit ef8f2a1

File tree

2 files changed

+226
-1
lines changed

2 files changed

+226
-1
lines changed

util/gnmi.go

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ func PathMatchesPrefix(path *gpb.Path, prefix []string) bool {
4343
}
4444

4545
// PathElemsEqual replaces the proto.Equal() check for PathElems.
46+
// If a.Key["foo"] == "*" and b.Key["foo"] == "bar" func returns false.
4647
// This significantly improves comparison speed.
4748
func PathElemsEqual(a, b *gpb.PathElem) bool {
4849
// This check allows avoiding to deal with any null PathElems later on.
@@ -53,12 +54,13 @@ func PathElemsEqual(a, b *gpb.PathElem) bool {
5354
if a.Name != b.Name {
5455
return false
5556
}
57+
5658
if len(a.Key) != len(b.Key) {
5759
return false
5860
}
5961

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

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

100+
// PathMatchesQuery returns whether query is prefix of path.
101+
// Only the query may contain wildcard name or keys.
102+
// TODO: Multilevel wildcards ("...") not supported.
103+
// If either path and query contain nil elements func returns false.
104+
// Both paths must use the gNMI >=0.4.0 PathElem path format.
105+
func PathMatchesQuery(path, query *gpb.Path) bool {
106+
if len(path.GetElem()) < len(query.GetElem()) || path.Origin != query.Origin {
107+
return false
108+
}
109+
for i, queryElem := range query.Elem {
110+
pathElem := path.Elem[i]
111+
if queryElem == nil || pathElem == nil {
112+
return false
113+
}
114+
if queryElem.Name != "*" && queryElem.Name != pathElem.Name {
115+
return false
116+
}
117+
for qk, qv := range queryElem.Key {
118+
if pv, ok := pathElem.Key[qk]; !ok || (qv != "*" && qv != pv) {
119+
return false
120+
}
121+
}
122+
}
123+
return true
124+
}
125+
95126
// TrimGNMIPathPrefix returns path with the prefix trimmed. It returns the
96127
// original path if the prefix does not fully match.
97128
func TrimGNMIPathPrefix(path *gpb.Path, prefix []string) *gpb.Path {

util/gnmi_test.go

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,200 @@ func TestPathMatchesPathElemPrefix(t *testing.T) {
526526
}
527527
}
528528

529+
func TestPathMatchesQuery(t *testing.T) {
530+
tests := []struct {
531+
desc string
532+
inPath *gpb.Path
533+
inQuery *gpb.Path
534+
want bool
535+
}{{
536+
desc: "valid query with no keys",
537+
inPath: &gpb.Path{
538+
Elem: []*gpb.PathElem{{
539+
Name: "one",
540+
}, {
541+
Name: "two",
542+
}},
543+
},
544+
inQuery: &gpb.Path{
545+
Elem: []*gpb.PathElem{{
546+
Name: "one",
547+
}},
548+
},
549+
want: true,
550+
}, {
551+
desc: "valid query with wildcard name",
552+
inPath: &gpb.Path{
553+
Elem: []*gpb.PathElem{{
554+
Name: "one",
555+
}, {
556+
Name: "two",
557+
}},
558+
},
559+
inQuery: &gpb.Path{
560+
Elem: []*gpb.PathElem{{
561+
Name: "*",
562+
}, {
563+
Name: "two",
564+
}},
565+
},
566+
want: true,
567+
}, {
568+
desc: "valid query with exact key match",
569+
inPath: &gpb.Path{
570+
Elem: []*gpb.PathElem{{
571+
Name: "one",
572+
Key: map[string]string{"two": "three"},
573+
}, {
574+
Name: "four",
575+
}},
576+
},
577+
inQuery: &gpb.Path{
578+
Elem: []*gpb.PathElem{{
579+
Name: "one",
580+
Key: map[string]string{"two": "three"},
581+
}},
582+
},
583+
want: true,
584+
}, {
585+
desc: "valid query with wildcard keys",
586+
inPath: &gpb.Path{
587+
Elem: []*gpb.PathElem{{
588+
Name: "one",
589+
Key: map[string]string{"two": "three"},
590+
}, {
591+
Name: "four",
592+
}},
593+
},
594+
inQuery: &gpb.Path{
595+
Elem: []*gpb.PathElem{{
596+
Name: "one",
597+
Key: map[string]string{"two": "*"},
598+
}},
599+
},
600+
want: true,
601+
}, {
602+
desc: "valid query with no keys and path with keys",
603+
inPath: &gpb.Path{
604+
Elem: []*gpb.PathElem{{
605+
Name: "one",
606+
Key: map[string]string{"two": "three"},
607+
}, {
608+
Name: "four",
609+
}},
610+
},
611+
inQuery: &gpb.Path{
612+
Elem: []*gpb.PathElem{{
613+
Name: "one",
614+
}},
615+
},
616+
want: true,
617+
}, {
618+
desc: "valid query with both missing and wildcard keys",
619+
inPath: &gpb.Path{
620+
Elem: []*gpb.PathElem{{
621+
Name: "one",
622+
Key: map[string]string{
623+
"two": "three",
624+
"four": "five",
625+
},
626+
}, {
627+
Name: "four",
628+
}},
629+
},
630+
inQuery: &gpb.Path{
631+
Elem: []*gpb.PathElem{{
632+
Name: "one",
633+
Key: map[string]string{"four": "*"},
634+
}},
635+
},
636+
want: true,
637+
}, {
638+
desc: "invalid nil elements",
639+
inPath: &gpb.Path{
640+
Elem: []*gpb.PathElem{
641+
nil,
642+
{
643+
Name: "twelve",
644+
}},
645+
},
646+
inQuery: &gpb.Path{
647+
Elem: []*gpb.PathElem{{
648+
Name: "three",
649+
}},
650+
},
651+
}, {
652+
desc: "invalid names not equal",
653+
inPath: &gpb.Path{
654+
Elem: []*gpb.PathElem{{
655+
Name: "fourteen",
656+
}, {
657+
Name: "twelve",
658+
}},
659+
},
660+
inQuery: &gpb.Path{
661+
Elem: []*gpb.PathElem{{
662+
Name: "three",
663+
}},
664+
},
665+
}, {
666+
desc: "invalid origin",
667+
inPath: &gpb.Path{
668+
Origin: "openconfig",
669+
Elem: []*gpb.PathElem{{
670+
Name: "one",
671+
}, {
672+
Name: "two",
673+
}},
674+
},
675+
inQuery: &gpb.Path{
676+
Origin: "google",
677+
Elem: []*gpb.PathElem{{
678+
Name: "one",
679+
}},
680+
},
681+
}, {
682+
desc: "invalid keys",
683+
inPath: &gpb.Path{
684+
Elem: []*gpb.PathElem{{
685+
Name: "three",
686+
Key: map[string]string{"four": "five"},
687+
}, {
688+
Name: "six",
689+
}},
690+
},
691+
inQuery: &gpb.Path{
692+
Elem: []*gpb.PathElem{{
693+
Name: "three",
694+
Key: map[string]string{"seven": "eight"},
695+
}},
696+
},
697+
}, {
698+
desc: "invalid missing wildcard keys",
699+
inPath: &gpb.Path{
700+
Elem: []*gpb.PathElem{{
701+
Name: "three",
702+
Key: map[string]string{"four": "five"},
703+
}, {
704+
Name: "six",
705+
}},
706+
},
707+
inQuery: &gpb.Path{
708+
Elem: []*gpb.PathElem{{
709+
Name: "three",
710+
Key: map[string]string{"seven": "*"},
711+
}},
712+
},
713+
}}
714+
for _, tt := range tests {
715+
t.Run(tt.desc, func(t *testing.T) {
716+
if got := PathMatchesQuery(tt.inPath, tt.inQuery); got != tt.want {
717+
t.Fatalf("did not get expected result, got: %v, want: %v", got, tt.want)
718+
}
719+
})
720+
}
721+
}
722+
529723
func TestTrimGNMIPathElemPrefix(t *testing.T) {
530724
tests := []struct {
531725
desc string

0 commit comments

Comments
 (0)