agent: implement HTTP endpoint
This commit is contained in:
parent
a48ff54318
commit
b961bab08c
|
@ -48,6 +48,7 @@ func init() {
|
|||
registerEndpoint("/v1/connect/ca/roots", []string{"GET"}, (*HTTPServer).ConnectCARoots)
|
||||
registerEndpoint("/v1/connect/intentions", []string{"GET", "POST"}, (*HTTPServer).IntentionEndpoint)
|
||||
registerEndpoint("/v1/connect/intentions/match", []string{"GET"}, (*HTTPServer).IntentionMatch)
|
||||
registerEndpoint("/v1/connect/intentions/test", []string{"GET"}, (*HTTPServer).IntentionTest)
|
||||
registerEndpoint("/v1/connect/intentions/", []string{"GET", "PUT", "DELETE"}, (*HTTPServer).IntentionSpecific)
|
||||
registerEndpoint("/v1/coordinate/datacenters", []string{"GET"}, (*HTTPServer).CoordinateDatacenters)
|
||||
registerEndpoint("/v1/coordinate/nodes", []string{"GET"}, (*HTTPServer).CoordinateNodes)
|
||||
|
|
|
@ -122,6 +122,59 @@ func (s *HTTPServer) IntentionMatch(resp http.ResponseWriter, req *http.Request)
|
|||
return response, nil
|
||||
}
|
||||
|
||||
// GET /v1/connect/intentions/test
|
||||
func (s *HTTPServer) IntentionTest(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||||
// Prepare args
|
||||
args := &structs.IntentionQueryRequest{Test: &structs.IntentionQueryTest{}}
|
||||
if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
q := req.URL.Query()
|
||||
|
||||
// Set the source type if set
|
||||
args.Test.SourceType = structs.IntentionSourceConsul
|
||||
if sourceType, ok := q["source-type"]; ok && len(sourceType) > 0 {
|
||||
args.Test.SourceType = structs.IntentionSourceType(sourceType[0])
|
||||
}
|
||||
|
||||
// Extract the source/destination
|
||||
source, ok := q["source"]
|
||||
if !ok || len(source) != 1 {
|
||||
return nil, fmt.Errorf("required query parameter 'source' not set")
|
||||
}
|
||||
destination, ok := q["destination"]
|
||||
if !ok || len(destination) != 1 {
|
||||
return nil, fmt.Errorf("required query parameter 'destination' not set")
|
||||
}
|
||||
|
||||
// We parse them the same way as matches to extract namespace/name
|
||||
args.Test.SourceName = source[0]
|
||||
if args.Test.SourceType == structs.IntentionSourceConsul {
|
||||
entry, err := parseIntentionMatchEntry(source[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("source %q is invalid: %s", source[0], err)
|
||||
}
|
||||
args.Test.SourceNS = entry.Namespace
|
||||
args.Test.SourceName = entry.Name
|
||||
}
|
||||
|
||||
// The destination is always in the Consul format
|
||||
entry, err := parseIntentionMatchEntry(destination[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("destination %q is invalid: %s", destination[0], err)
|
||||
}
|
||||
args.Test.DestinationNS = entry.Namespace
|
||||
args.Test.DestinationName = entry.Name
|
||||
|
||||
var reply structs.IntentionQueryTestResponse
|
||||
if err := s.agent.RPC("Intention.Test", args, &reply); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &reply, nil
|
||||
}
|
||||
|
||||
// IntentionSpecific handles the endpoint for /v1/connection/intentions/:id
|
||||
func (s *HTTPServer) IntentionSpecific(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||||
id := strings.TrimPrefix(req.URL.Path, "/v1/connect/intentions/")
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
|
||||
"github.com/hashicorp/consul/agent/structs"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestIntentionsList_empty(t *testing.T) {
|
||||
|
@ -180,6 +181,96 @@ func TestIntentionsMatch_noName(t *testing.T) {
|
|||
assert.Nil(obj)
|
||||
}
|
||||
|
||||
func TestIntentionsTest_basic(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require := require.New(t)
|
||||
a := NewTestAgent(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.IntentionTest(resp, req)
|
||||
require.Nil(err)
|
||||
value := obj.(*structs.IntentionQueryTestResponse)
|
||||
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.IntentionTest(resp, req)
|
||||
require.Nil(err)
|
||||
value := obj.(*structs.IntentionQueryTestResponse)
|
||||
require.True(value.Allowed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntentionsTest_noSource(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require := require.New(t)
|
||||
a := NewTestAgent(t.Name(), "")
|
||||
defer a.Shutdown()
|
||||
|
||||
// Request
|
||||
req, _ := http.NewRequest("GET",
|
||||
"/v1/connect/intentions/test?destination=B", nil)
|
||||
resp := httptest.NewRecorder()
|
||||
obj, err := a.srv.IntentionTest(resp, req)
|
||||
require.NotNil(err)
|
||||
require.Contains(err.Error(), "'source' not set")
|
||||
require.Nil(obj)
|
||||
}
|
||||
|
||||
func TestIntentionsTest_noDestination(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require := require.New(t)
|
||||
a := NewTestAgent(t.Name(), "")
|
||||
defer a.Shutdown()
|
||||
|
||||
// Request
|
||||
req, _ := http.NewRequest("GET",
|
||||
"/v1/connect/intentions/test?source=B", nil)
|
||||
resp := httptest.NewRecorder()
|
||||
obj, err := a.srv.IntentionTest(resp, req)
|
||||
require.NotNil(err)
|
||||
require.Contains(err.Error(), "'destination' not set")
|
||||
require.Nil(obj)
|
||||
}
|
||||
|
||||
func TestIntentionsCreate_good(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
Loading…
Reference in New Issue