mirror of https://github.com/status-im/consul.git
agent/cache-types: support intention match queries
This commit is contained in:
parent
e1c1b8812a
commit
0f3f3d13ca
|
@ -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
|
||||||
|
}
|
|
@ -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")
|
||||||
|
|
||||||
|
}
|
|
@ -2,10 +2,13 @@ package structs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/agent/cache"
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
|
"github.com/mitchellh/hashstructure"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -267,6 +270,36 @@ func (q *IntentionQueryRequest) RequestDatacenter() string {
|
||||||
return q.Datacenter
|
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
|
// IntentionQueryMatch are the parameters for performing a match request
|
||||||
// against the state store.
|
// against the state store.
|
||||||
type IntentionQueryMatch struct {
|
type IntentionQueryMatch struct {
|
||||||
|
|
Loading…
Reference in New Issue