agent/cache-types: support intention match queries

This commit is contained in:
Mitchell Hashimoto 2018-04-17 18:18:16 -05:00
parent 109bb946e9
commit 56774f24d0
No known key found for this signature in database
GPG key ID: 744E147AA52F5B0A
3 changed files with 131 additions and 0 deletions

View file

@ -0,0 +1,41 @@
package cachetype
import (
"fmt"
"github.com/hashicorp/consul/agent/cache"
"github.com/hashicorp/consul/agent/structs"
)
// Recommended name for registration.
const IntentionMatchName = "intention-match"
// IntentionMatch supports fetching the intentions via match queries.
type IntentionMatch struct {
RPC RPC
}
func (c *IntentionMatch) Fetch(opts cache.FetchOptions, req cache.Request) (cache.FetchResult, error) {
var result cache.FetchResult
// The request should be an IntentionQueryRequest.
reqReal, ok := req.(*structs.IntentionQueryRequest)
if !ok {
return result, fmt.Errorf(
"Internal cache failure: request wrong type: %T", req)
}
// Set the minimum query index to our current index so we block
reqReal.MinQueryIndex = opts.MinIndex
reqReal.MaxQueryTime = opts.Timeout
// Fetch
var reply structs.IndexedIntentionMatches
if err := c.RPC.RPC("Intention.Match", reqReal, &reply); err != nil {
return result, err
}
result.Value = &reply
result.Index = reply.Index
return result, nil
}

View file

@ -0,0 +1,57 @@
package cachetype
import (
"testing"
"time"
"github.com/hashicorp/consul/agent/cache"
"github.com/hashicorp/consul/agent/structs"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
func TestIntentionMatch(t *testing.T) {
require := require.New(t)
rpc := TestRPC(t)
defer rpc.AssertExpectations(t)
typ := &IntentionMatch{RPC: rpc}
// Expect the proper RPC call. This also sets the expected value
// since that is return-by-pointer in the arguments.
var resp *structs.IndexedIntentionMatches
rpc.On("RPC", "Intention.Match", mock.Anything, mock.Anything).Return(nil).
Run(func(args mock.Arguments) {
req := args.Get(1).(*structs.IntentionQueryRequest)
require.Equal(uint64(24), req.MinQueryIndex)
require.Equal(1*time.Second, req.MaxQueryTime)
reply := args.Get(2).(*structs.IndexedIntentionMatches)
reply.Index = 48
resp = reply
})
// Fetch
result, err := typ.Fetch(cache.FetchOptions{
MinIndex: 24,
Timeout: 1 * time.Second,
}, &structs.IntentionQueryRequest{Datacenter: "dc1"})
require.Nil(err)
require.Equal(cache.FetchResult{
Value: resp,
Index: 48,
}, result)
}
func TestIntentionMatch_badReqType(t *testing.T) {
require := require.New(t)
rpc := TestRPC(t)
defer rpc.AssertExpectations(t)
typ := &IntentionMatch{RPC: rpc}
// Fetch
_, err := typ.Fetch(cache.FetchOptions{}, cache.TestRequest(
t, cache.RequestInfo{Key: "foo", MinIndex: 64}))
require.NotNil(err)
require.Contains(err.Error(), "wrong type")
}

View file

@ -2,10 +2,13 @@ package structs
import (
"fmt"
"strconv"
"strings"
"time"
"github.com/hashicorp/consul/agent/cache"
"github.com/hashicorp/go-multierror"
"github.com/mitchellh/hashstructure"
)
const (
@ -267,6 +270,36 @@ func (q *IntentionQueryRequest) RequestDatacenter() string {
return q.Datacenter
}
// cache.Request impl.
func (q *IntentionQueryRequest) CacheInfo() cache.RequestInfo {
// We only support caching Match queries, so if Match isn't set,
// then return an empty info object which will cause a pass-through
// (and likely fail).
if q.Match == nil {
return cache.RequestInfo{}
}
info := cache.RequestInfo{
Token: q.Token,
Datacenter: q.Datacenter,
MinIndex: q.MinQueryIndex,
Timeout: q.MaxQueryTime,
}
// Calculate the cache key via just hashing the Match struct. This
// has been configured so things like ordering of entries has no
// effect (via struct tags).
v, err := hashstructure.Hash(q.Match, nil)
if err == nil {
// If there is an error, we don't set the key. A blank key forces
// no cache for this request so the request is forwarded directly
// to the server.
info.Key = strconv.FormatUint(v, 10)
}
return info
}
// IntentionQueryMatch are the parameters for performing a match request
// against the state store.
type IntentionQueryMatch struct {