command/intention/finder: package for finding based on src/dst

This commit is contained in:
Mitchell Hashimoto 2018-05-11 21:42:46 -07:00
parent a1a7eaa876
commit 77d0360de1
No known key found for this signature in database
GPG key ID: 744E147AA52F5B0A
3 changed files with 117 additions and 9 deletions

View file

@ -53,17 +53,30 @@ type Intention struct {
// String returns human-friendly output describing ths intention. // String returns human-friendly output describing ths intention.
func (i *Intention) String() string { func (i *Intention) String() string {
source := i.SourceName return fmt.Sprintf("%s => %s (%s)",
if i.SourceNS != "" { i.SourceString(),
source = i.SourceNS + "/" + source i.DestinationString(),
i.Action)
}
// SourceString returns the namespace/name format for the source, or
// just "name" if the namespace is the default namespace.
func (i *Intention) SourceString() string {
return i.partString(i.SourceNS, i.SourceName)
}
// DestinationString returns the namespace/name format for the source, or
// just "name" if the namespace is the default namespace.
func (i *Intention) DestinationString() string {
return i.partString(i.DestinationNS, i.DestinationName)
}
func (i *Intention) partString(ns, n string) string {
if ns != "" {
n = ns + "/" + n
} }
dest := i.DestinationName return n
if i.DestinationNS != "" {
dest = i.DestinationNS + "/" + dest
}
return fmt.Sprintf("%s => %s (%s)", source, dest, i.Action)
} }
// IntentionAction is the action that the intention represents. This // IntentionAction is the action that the intention represents. This

View file

@ -0,0 +1,46 @@
package finder
import (
"sync"
"github.com/hashicorp/consul/api"
)
// Finder finds intentions by a src/dst exact match. There is currently
// no direct API to do this so this struct downloads all intentions and
// caches them once, and searches in-memory for this. For now this works since
// even with a very large number of intentions, the size of the data gzipped
// over HTTP will be relatively small.
type Finder struct {
// Client is the API client to use for any requests.
Client *api.Client
lock sync.Mutex
ixns []*api.Intention // cached list of intentions
}
// Find finds the intention that matches the given src and dst. This will
// return nil when the result is not found.
func (f *Finder) Find(src, dst string) (*api.Intention, error) {
f.lock.Lock()
defer f.lock.Unlock()
// If the list of ixns is nil, then we haven't fetched yet, so fetch
if f.ixns == nil {
ixns, _, err := f.Client.Connect().Intentions(nil)
if err != nil {
return nil, err
}
f.ixns = ixns
}
// Go through the intentions and find an exact match
for _, ixn := range f.ixns {
if ixn.SourceString() == src && ixn.DestinationString() == dst {
return ixn, nil
}
}
return nil, nil
}

View file

@ -0,0 +1,49 @@
package finder
import (
"testing"
"github.com/hashicorp/consul/agent"
"github.com/hashicorp/consul/api"
"github.com/stretchr/testify/require"
)
func TestFinder(t *testing.T) {
t.Parallel()
require := require.New(t)
a := agent.NewTestAgent(t.Name(), ``)
defer a.Shutdown()
client := a.Client()
// Create a set of intentions
var ids []string
{
insert := [][]string{
[]string{"a", "b", "c", "d"},
}
for _, v := range insert {
ixn := &api.Intention{
SourceNS: v[0],
SourceName: v[1],
DestinationNS: v[2],
DestinationName: v[3],
Action: api.IntentionActionAllow,
}
id, _, err := client.Connect().IntentionCreate(ixn, nil)
require.NoError(err)
ids = append(ids, id)
}
}
finder := &Finder{Client: client}
ixn, err := finder.Find("a/b", "c/d")
require.NoError(err)
require.Equal(ids[0], ixn.ID)
ixn, err = finder.Find("a/c", "c/d")
require.NoError(err)
require.Nil(ixn)
}