agent/structs: IntentionPrecedenceSorter for sorting based on precedence
This commit is contained in:
parent
a91fadb971
commit
231f7328bd
|
@ -4,6 +4,11 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// IntentionWildcard is the wildcard value.
|
||||||
|
IntentionWildcard = "*"
|
||||||
|
)
|
||||||
|
|
||||||
// Intention defines an intention for the Connect Service Graph. This defines
|
// Intention defines an intention for the Connect Service Graph. This defines
|
||||||
// the allowed or denied behavior of a connection between two services using
|
// the allowed or denied behavior of a connection between two services using
|
||||||
// Connect.
|
// Connect.
|
||||||
|
@ -100,6 +105,16 @@ func (q *IntentionRequest) RequestDatacenter() string {
|
||||||
return q.Datacenter
|
return q.Datacenter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IntentionMatchType is the target for a match request. For example,
|
||||||
|
// matching by source will look for all intentions that match the given
|
||||||
|
// source value.
|
||||||
|
type IntentionMatchType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
IntentionMatchSource IntentionMatchType = "source"
|
||||||
|
IntentionMatchDestination IntentionMatchType = "destination"
|
||||||
|
)
|
||||||
|
|
||||||
// IntentionQueryRequest is used to query intentions.
|
// IntentionQueryRequest is used to query intentions.
|
||||||
type IntentionQueryRequest struct {
|
type IntentionQueryRequest struct {
|
||||||
// Datacenter is the target this request is intended for.
|
// Datacenter is the target this request is intended for.
|
||||||
|
@ -108,6 +123,12 @@ type IntentionQueryRequest struct {
|
||||||
// IntentionID is the ID of a specific intention.
|
// IntentionID is the ID of a specific intention.
|
||||||
IntentionID string
|
IntentionID string
|
||||||
|
|
||||||
|
// MatchBy and MatchNames are used to match a namespace/name pair
|
||||||
|
// to a set of intentions. The list of MatchNames is an OR list,
|
||||||
|
// all matching intentions are returned together.
|
||||||
|
MatchBy IntentionMatchType
|
||||||
|
MatchNames []string
|
||||||
|
|
||||||
// Options for queries
|
// Options for queries
|
||||||
QueryOptions
|
QueryOptions
|
||||||
}
|
}
|
||||||
|
@ -116,3 +137,62 @@ type IntentionQueryRequest struct {
|
||||||
func (q *IntentionQueryRequest) RequestDatacenter() string {
|
func (q *IntentionQueryRequest) RequestDatacenter() string {
|
||||||
return q.Datacenter
|
return q.Datacenter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IntentionQueryMatch are the parameters for performing a match request
|
||||||
|
// against the state store.
|
||||||
|
type IntentionQueryMatch struct {
|
||||||
|
Type IntentionMatchType
|
||||||
|
Entries []IntentionMatchEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntentionMatchEntry is a single entry for matching an intention.
|
||||||
|
type IntentionMatchEntry struct {
|
||||||
|
Namespace string
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntentionPrecedenceSorter takes a list of intentions and sorts them
|
||||||
|
// based on the match precedence rules for intentions. The intentions
|
||||||
|
// closer to the head of the list have higher precedence. i.e. index 0 has
|
||||||
|
// the highest precedence.
|
||||||
|
type IntentionPrecedenceSorter Intentions
|
||||||
|
|
||||||
|
func (s IntentionPrecedenceSorter) Len() int { return len(s) }
|
||||||
|
func (s IntentionPrecedenceSorter) Swap(i, j int) {
|
||||||
|
s[i], s[j] = s[j], s[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s IntentionPrecedenceSorter) Less(i, j int) bool {
|
||||||
|
a, b := s[i], s[j]
|
||||||
|
|
||||||
|
// First test the # of exact values in destination, since precedence
|
||||||
|
// is destination-oriented.
|
||||||
|
aExact := s.countExact(a.DestinationNS, a.DestinationName)
|
||||||
|
bExact := s.countExact(b.DestinationNS, b.DestinationName)
|
||||||
|
if aExact != bExact {
|
||||||
|
return aExact > bExact
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next test the # of exact values in source
|
||||||
|
aExact = s.countExact(a.SourceNS, a.SourceName)
|
||||||
|
bExact = s.countExact(b.SourceNS, b.SourceName)
|
||||||
|
return aExact > bExact
|
||||||
|
}
|
||||||
|
|
||||||
|
// countExact counts the number of exact values (not wildcards) in
|
||||||
|
// the given namespace and name.
|
||||||
|
func (s IntentionPrecedenceSorter) countExact(ns, n string) int {
|
||||||
|
// If NS is wildcard, it must be zero since wildcards only follow exact
|
||||||
|
if ns == IntentionWildcard {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same reasoning as above, a wildcard can only follow an exact value
|
||||||
|
// and an exact value cannot follow a wildcard, so if name is a wildcard
|
||||||
|
// we must have exactly one.
|
||||||
|
if n == IntentionWildcard {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
|
72
agent/structs/intention_test.go
Normal file
72
agent/structs/intention_test.go
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
package structs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIntentionPrecedenceSorter(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
Name string
|
||||||
|
Input [][]string // SrcNS, SrcN, DstNS, DstN
|
||||||
|
Expected [][]string // Same structure as Input
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"exhaustive list",
|
||||||
|
[][]string{
|
||||||
|
{"*", "*", "exact", "*"},
|
||||||
|
{"*", "*", "*", "*"},
|
||||||
|
{"exact", "*", "exact", "exact"},
|
||||||
|
{"*", "*", "exact", "exact"},
|
||||||
|
{"exact", "exact", "*", "*"},
|
||||||
|
{"exact", "exact", "exact", "exact"},
|
||||||
|
{"exact", "exact", "exact", "*"},
|
||||||
|
{"exact", "*", "exact", "*"},
|
||||||
|
{"exact", "*", "*", "*"},
|
||||||
|
},
|
||||||
|
[][]string{
|
||||||
|
{"exact", "exact", "exact", "exact"},
|
||||||
|
{"exact", "*", "exact", "exact"},
|
||||||
|
{"*", "*", "exact", "exact"},
|
||||||
|
{"exact", "exact", "exact", "*"},
|
||||||
|
{"exact", "*", "exact", "*"},
|
||||||
|
{"*", "*", "exact", "*"},
|
||||||
|
{"exact", "exact", "*", "*"},
|
||||||
|
{"exact", "*", "*", "*"},
|
||||||
|
{"*", "*", "*", "*"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.Name, func(t *testing.T) {
|
||||||
|
var input Intentions
|
||||||
|
for _, v := range tc.Input {
|
||||||
|
input = append(input, &Intention{
|
||||||
|
SourceNS: v[0],
|
||||||
|
SourceName: v[1],
|
||||||
|
DestinationNS: v[2],
|
||||||
|
DestinationName: v[3],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort
|
||||||
|
sort.Sort(IntentionPrecedenceSorter(input))
|
||||||
|
|
||||||
|
// Get back into a comparable form
|
||||||
|
var actual [][]string
|
||||||
|
for _, v := range input {
|
||||||
|
actual = append(actual, []string{
|
||||||
|
v.SourceNS,
|
||||||
|
v.SourceName,
|
||||||
|
v.DestinationNS,
|
||||||
|
v.DestinationName,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(actual, tc.Expected) {
|
||||||
|
t.Fatalf("bad (got, wanted):\n\n%#v\n\n%#v", actual, tc.Expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue