a34f8c751e
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
520 lines
12 KiB
Go
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)
|
|
})
|
|
}
|
|
}
|