open-consul/agent/intentions_endpoint_test.go
Matt Keeler a34f8c751e
Pass a testing.T into NewTestAgent and TestAgent.Start (#5342)
This way we can avoid unnecessary panics which cause other tests not to run.

This doesn't remove all the possibilities for panics causing other tests not to run, it just fixes the TestAgent
2019-02-14 10:59:14 -05:00

520 lines
12 KiB
Go

package agent
import (
"fmt"
"net/http"
"net/http/httptest"
"testing"
"github.com/hashicorp/consul/agent/structs"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestIntentionsList_empty(t *testing.T) {
t.Parallel()
assert := assert.New(t)
a := NewTestAgent(t, t.Name(), "")
defer a.Shutdown()
// Make sure an empty list is non-nil.
req, _ := http.NewRequest("GET", "/v1/connect/intentions", nil)
resp := httptest.NewRecorder()
obj, err := a.srv.IntentionList(resp, req)
assert.Nil(err)
value := obj.(structs.Intentions)
assert.NotNil(value)
assert.Len(value, 0)
}
func TestIntentionsList_values(t *testing.T) {
t.Parallel()
assert := assert.New(t)
a := NewTestAgent(t, t.Name(), "")
defer a.Shutdown()
// Create some intentions, note we create the lowest precedence first to test
// sorting.
for _, v := range []string{"*", "foo", "bar"} {
req := structs.IntentionRequest{
Datacenter: "dc1",
Op: structs.IntentionOpCreate,
Intention: structs.TestIntention(t),
}
req.Intention.SourceName = v
var reply string
assert.Nil(a.RPC("Intention.Apply", &req, &reply))
}
// Request
req, _ := http.NewRequest("GET", "/v1/connect/intentions", nil)
resp := httptest.NewRecorder()
obj, err := a.srv.IntentionList(resp, req)
assert.NoError(err)
value := obj.(structs.Intentions)
assert.Len(value, 3)
expected := []string{"bar", "foo", "*"}
actual := []string{
value[0].SourceName,
value[1].SourceName,
value[2].SourceName,
}
assert.Equal(expected, actual)
}
func TestIntentionsMatch_basic(t *testing.T) {
t.Parallel()
assert := assert.New(t)
a := NewTestAgent(t, t.Name(), "")
defer a.Shutdown()
// Create some intentions
{
insert := [][]string{
{"foo", "*", "foo", "*"},
{"foo", "*", "foo", "bar"},
{"foo", "*", "foo", "baz"}, // shouldn't match
{"foo", "*", "bar", "bar"}, // shouldn't match
{"foo", "*", "bar", "*"}, // shouldn't match
{"foo", "*", "*", "*"},
{"bar", "*", "foo", "bar"}, // duplicate destination different source
}
for _, v := range insert {
ixn := structs.IntentionRequest{
Datacenter: "dc1",
Op: structs.IntentionOpCreate,
Intention: structs.TestIntention(t),
}
ixn.Intention.SourceNS = v[0]
ixn.Intention.SourceName = v[1]
ixn.Intention.DestinationNS = v[2]
ixn.Intention.DestinationName = v[3]
// Create
var reply string
assert.Nil(a.RPC("Intention.Apply", &ixn, &reply))
}
}
// Request
req, _ := http.NewRequest("GET",
"/v1/connect/intentions/match?by=destination&name=foo/bar", nil)
resp := httptest.NewRecorder()
obj, err := a.srv.IntentionMatch(resp, req)
assert.Nil(err)
value := obj.(map[string]structs.Intentions)
assert.Len(value, 1)
var actual [][]string
expected := [][]string{
{"bar", "*", "foo", "bar"},
{"foo", "*", "foo", "bar"},
{"foo", "*", "foo", "*"},
{"foo", "*", "*", "*"},
}
for _, ixn := range value["foo/bar"] {
actual = append(actual, []string{
ixn.SourceNS,
ixn.SourceName,
ixn.DestinationNS,
ixn.DestinationName,
})
}
assert.Equal(expected, actual)
}
func TestIntentionsMatch_noBy(t *testing.T) {
t.Parallel()
assert := assert.New(t)
a := NewTestAgent(t, t.Name(), "")
defer a.Shutdown()
// Request
req, _ := http.NewRequest("GET",
"/v1/connect/intentions/match?name=foo/bar", nil)
resp := httptest.NewRecorder()
obj, err := a.srv.IntentionMatch(resp, req)
assert.NotNil(err)
assert.Contains(err.Error(), "by")
assert.Nil(obj)
}
func TestIntentionsMatch_byInvalid(t *testing.T) {
t.Parallel()
assert := assert.New(t)
a := NewTestAgent(t, t.Name(), "")
defer a.Shutdown()
// Request
req, _ := http.NewRequest("GET",
"/v1/connect/intentions/match?by=datacenter", nil)
resp := httptest.NewRecorder()
obj, err := a.srv.IntentionMatch(resp, req)
assert.NotNil(err)
assert.Contains(err.Error(), "'by' parameter")
assert.Nil(obj)
}
func TestIntentionsMatch_noName(t *testing.T) {
t.Parallel()
assert := assert.New(t)
a := NewTestAgent(t, t.Name(), "")
defer a.Shutdown()
// Request
req, _ := http.NewRequest("GET",
"/v1/connect/intentions/match?by=source", nil)
resp := httptest.NewRecorder()
obj, err := a.srv.IntentionMatch(resp, req)
assert.NotNil(err)
assert.Contains(err.Error(), "'name' not set")
assert.Nil(obj)
}
func TestIntentionsCheck_basic(t *testing.T) {
t.Parallel()
require := require.New(t)
a := NewTestAgent(t, t.Name(), "")
defer a.Shutdown()
// Create some intentions
{
insert := [][]string{
{"foo", "*", "foo", "*"},
{"foo", "*", "foo", "bar"},
{"bar", "*", "foo", "bar"},
}
for _, v := range insert {
ixn := structs.IntentionRequest{
Datacenter: "dc1",
Op: structs.IntentionOpCreate,
Intention: structs.TestIntention(t),
}
ixn.Intention.SourceNS = v[0]
ixn.Intention.SourceName = v[1]
ixn.Intention.DestinationNS = v[2]
ixn.Intention.DestinationName = v[3]
ixn.Intention.Action = structs.IntentionActionDeny
// Create
var reply string
require.Nil(a.RPC("Intention.Apply", &ixn, &reply))
}
}
// Request matching intention
{
req, _ := http.NewRequest("GET",
"/v1/connect/intentions/test?source=foo/bar&destination=foo/baz", nil)
resp := httptest.NewRecorder()
obj, err := a.srv.IntentionCheck(resp, req)
require.Nil(err)
value := obj.(*structs.IntentionQueryCheckResponse)
require.False(value.Allowed)
}
// Request non-matching intention
{
req, _ := http.NewRequest("GET",
"/v1/connect/intentions/test?source=foo/bar&destination=bar/qux", nil)
resp := httptest.NewRecorder()
obj, err := a.srv.IntentionCheck(resp, req)
require.Nil(err)
value := obj.(*structs.IntentionQueryCheckResponse)
require.True(value.Allowed)
}
}
func TestIntentionsCheck_noSource(t *testing.T) {
t.Parallel()
require := require.New(t)
a := NewTestAgent(t, t.Name(), "")
defer a.Shutdown()
// Request
req, _ := http.NewRequest("GET",
"/v1/connect/intentions/test?destination=B", nil)
resp := httptest.NewRecorder()
obj, err := a.srv.IntentionCheck(resp, req)
require.NotNil(err)
require.Contains(err.Error(), "'source' not set")
require.Nil(obj)
}
func TestIntentionsCheck_noDestination(t *testing.T) {
t.Parallel()
require := require.New(t)
a := NewTestAgent(t, t.Name(), "")
defer a.Shutdown()
// Request
req, _ := http.NewRequest("GET",
"/v1/connect/intentions/test?source=B", nil)
resp := httptest.NewRecorder()
obj, err := a.srv.IntentionCheck(resp, req)
require.NotNil(err)
require.Contains(err.Error(), "'destination' not set")
require.Nil(obj)
}
func TestIntentionsCreate_good(t *testing.T) {
t.Parallel()
assert := assert.New(t)
a := NewTestAgent(t, t.Name(), "")
defer a.Shutdown()
// Make sure an empty list is non-nil.
args := structs.TestIntention(t)
args.SourceName = "foo"
req, _ := http.NewRequest("POST", "/v1/connect/intentions", jsonReader(args))
resp := httptest.NewRecorder()
obj, err := a.srv.IntentionCreate(resp, req)
assert.Nil(err)
value := obj.(intentionCreateResponse)
assert.NotEqual("", value.ID)
// Read the value
{
req := &structs.IntentionQueryRequest{
Datacenter: "dc1",
IntentionID: value.ID,
}
var resp structs.IndexedIntentions
assert.Nil(a.RPC("Intention.Get", req, &resp))
assert.Len(resp.Intentions, 1)
actual := resp.Intentions[0]
assert.Equal("foo", actual.SourceName)
}
}
func TestIntentionsCreate_noBody(t *testing.T) {
t.Parallel()
a := NewTestAgent(t, t.Name(), "")
defer a.Shutdown()
// Create with no body
req, _ := http.NewRequest("POST", "/v1/connect/intentions", nil)
resp := httptest.NewRecorder()
_, err := a.srv.IntentionCreate(resp, req)
require.Error(t, err)
}
func TestIntentionsSpecificGet_good(t *testing.T) {
t.Parallel()
assert := assert.New(t)
a := NewTestAgent(t, t.Name(), "")
defer a.Shutdown()
// The intention
ixn := structs.TestIntention(t)
// Create an intention directly
var reply string
{
req := structs.IntentionRequest{
Datacenter: "dc1",
Op: structs.IntentionOpCreate,
Intention: ixn,
}
assert.Nil(a.RPC("Intention.Apply", &req, &reply))
}
// Get the value
req, _ := http.NewRequest("GET", fmt.Sprintf("/v1/connect/intentions/%s", reply), nil)
resp := httptest.NewRecorder()
obj, err := a.srv.IntentionSpecific(resp, req)
assert.Nil(err)
value := obj.(*structs.Intention)
assert.Equal(reply, value.ID)
ixn.ID = value.ID
ixn.RaftIndex = value.RaftIndex
ixn.CreatedAt, ixn.UpdatedAt = value.CreatedAt, value.UpdatedAt
assert.Equal(ixn, value)
}
func TestIntentionsSpecificGet_invalidId(t *testing.T) {
t.Parallel()
require := require.New(t)
a := NewTestAgent(t, t.Name(), "")
defer a.Shutdown()
// Read intention with bad ID
req, _ := http.NewRequest("GET", "/v1/connect/intentions/hello", nil)
resp := httptest.NewRecorder()
obj, err := a.srv.IntentionSpecific(resp, req)
require.Nil(obj)
require.Error(err)
require.IsType(BadRequestError{}, err)
require.Contains(err.Error(), "UUID")
}
func TestIntentionsSpecificUpdate_good(t *testing.T) {
t.Parallel()
assert := assert.New(t)
a := NewTestAgent(t, t.Name(), "")
defer a.Shutdown()
// The intention
ixn := structs.TestIntention(t)
// Create an intention directly
var reply string
{
req := structs.IntentionRequest{
Datacenter: "dc1",
Op: structs.IntentionOpCreate,
Intention: ixn,
}
assert.Nil(a.RPC("Intention.Apply", &req, &reply))
}
// Update the intention
ixn.ID = "bogus"
ixn.SourceName = "bar"
req, _ := http.NewRequest("PUT", fmt.Sprintf("/v1/connect/intentions/%s", reply), jsonReader(ixn))
resp := httptest.NewRecorder()
obj, err := a.srv.IntentionSpecific(resp, req)
assert.Nil(err)
value := obj.(intentionCreateResponse)
assert.Equal(reply, value.ID)
// Read the value
{
req := &structs.IntentionQueryRequest{
Datacenter: "dc1",
IntentionID: reply,
}
var resp structs.IndexedIntentions
assert.Nil(a.RPC("Intention.Get", req, &resp))
assert.Len(resp.Intentions, 1)
actual := resp.Intentions[0]
assert.Equal("bar", actual.SourceName)
}
}
func TestIntentionsSpecificDelete_good(t *testing.T) {
t.Parallel()
assert := assert.New(t)
a := NewTestAgent(t, t.Name(), "")
defer a.Shutdown()
// The intention
ixn := structs.TestIntention(t)
ixn.SourceName = "foo"
// Create an intention directly
var reply string
{
req := structs.IntentionRequest{
Datacenter: "dc1",
Op: structs.IntentionOpCreate,
Intention: ixn,
}
assert.Nil(a.RPC("Intention.Apply", &req, &reply))
}
// Sanity check that the intention exists
{
req := &structs.IntentionQueryRequest{
Datacenter: "dc1",
IntentionID: reply,
}
var resp structs.IndexedIntentions
assert.Nil(a.RPC("Intention.Get", req, &resp))
assert.Len(resp.Intentions, 1)
actual := resp.Intentions[0]
assert.Equal("foo", actual.SourceName)
}
// Delete the intention
req, _ := http.NewRequest("DELETE", fmt.Sprintf("/v1/connect/intentions/%s", reply), nil)
resp := httptest.NewRecorder()
obj, err := a.srv.IntentionSpecific(resp, req)
assert.Nil(err)
assert.Equal(true, obj)
// Verify the intention is gone
{
req := &structs.IntentionQueryRequest{
Datacenter: "dc1",
IntentionID: reply,
}
var resp structs.IndexedIntentions
err := a.RPC("Intention.Get", req, &resp)
assert.NotNil(err)
assert.Contains(err.Error(), "not found")
}
}
func TestParseIntentionMatchEntry(t *testing.T) {
cases := []struct {
Input string
Expected structs.IntentionMatchEntry
Err bool
}{
{
"foo",
structs.IntentionMatchEntry{
Namespace: structs.IntentionDefaultNamespace,
Name: "foo",
},
false,
},
{
"foo/bar",
structs.IntentionMatchEntry{
Namespace: "foo",
Name: "bar",
},
false,
},
{
"foo/bar/baz",
structs.IntentionMatchEntry{},
true,
},
}
for _, tc := range cases {
t.Run(tc.Input, func(t *testing.T) {
assert := assert.New(t)
actual, err := parseIntentionMatchEntry(tc.Input)
assert.Equal(err != nil, tc.Err, err)
if err != nil {
return
}
assert.Equal(tc.Expected, actual)
})
}
}