mirror of https://github.com/status-im/consul.git
Sync enterprise changes to oss (#10994)
This commit updates OSS with files for enterprise-specific admin partitions feature work
This commit is contained in:
parent
06f3ccebce
commit
9bbfa048a2
|
@ -109,7 +109,7 @@ func (s *HTTPHandlers) configDelete(resp http.ResponseWriter, req *http.Request)
|
||||||
return reply, nil
|
return reply, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigCreate applies the given config entry update.
|
// ConfigApply applies the given config entry update.
|
||||||
func (s *HTTPHandlers) ConfigApply(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
func (s *HTTPHandlers) ConfigApply(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||||||
args := structs.ConfigEntryRequest{
|
args := structs.ConfigEntryRequest{
|
||||||
Op: structs.ConfigEntryUpsert,
|
Op: structs.ConfigEntryUpsert,
|
||||||
|
|
|
@ -59,6 +59,18 @@ func (s *Server) validateEnterpriseRequest(entMeta *structs.EnterpriseMeta, writ
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) validateEnterpriseIntentionPartition(partition string) error {
|
||||||
|
if partition == "" {
|
||||||
|
return nil
|
||||||
|
} else if strings.ToLower(partition) == "default" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// No special handling for wildcard partitions as they are pointless in OSS.
|
||||||
|
|
||||||
|
return errors.New("Partitions is a Consul Enterprise feature")
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) validateEnterpriseIntentionNamespace(ns string, _ bool) error {
|
func (s *Server) validateEnterpriseIntentionNamespace(ns string, _ bool) error {
|
||||||
if ns == "" {
|
if ns == "" {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -173,7 +173,7 @@ func (s *Intention) computeApplyChangesLegacyCreate(
|
||||||
) (*structs.IntentionMutation, error) {
|
) (*structs.IntentionMutation, error) {
|
||||||
// This variant is just for legacy UUID-based intentions.
|
// This variant is just for legacy UUID-based intentions.
|
||||||
|
|
||||||
args.Intention.DefaultNamespaces(entMeta)
|
args.Intention.FillPartitionAndNamespace(entMeta, true)
|
||||||
|
|
||||||
if !args.Intention.CanWrite(authz) {
|
if !args.Intention.CanWrite(authz) {
|
||||||
sn := args.Intention.SourceServiceName()
|
sn := args.Intention.SourceServiceName()
|
||||||
|
@ -257,12 +257,12 @@ func (s *Intention) computeApplyChangesLegacyUpdate(
|
||||||
return nil, acl.ErrPermissionDenied
|
return nil, acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
args.Intention.DefaultNamespaces(entMeta)
|
args.Intention.FillPartitionAndNamespace(entMeta, true)
|
||||||
|
|
||||||
// Prior to v1.9.0 renames of the destination side of an intention were
|
// Prior to v1.9.0 renames of the destination side of an intention were
|
||||||
// allowed, but that behavior doesn't work anymore.
|
// allowed, but that behavior doesn't work anymore.
|
||||||
if ixn.DestinationServiceName() != args.Intention.DestinationServiceName() {
|
if ixn.DestinationServiceName() != args.Intention.DestinationServiceName() {
|
||||||
return nil, fmt.Errorf("Cannot modify DestinationNS or DestinationName for an intention once it exists.")
|
return nil, fmt.Errorf("Cannot modify Destination partition/namespace/name for an intention once it exists.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default source type
|
// Default source type
|
||||||
|
@ -308,7 +308,7 @@ func (s *Intention) computeApplyChangesUpsert(
|
||||||
return nil, fmt.Errorf("ID must not be specified")
|
return nil, fmt.Errorf("ID must not be specified")
|
||||||
}
|
}
|
||||||
|
|
||||||
args.Intention.DefaultNamespaces(entMeta)
|
args.Intention.FillPartitionAndNamespace(entMeta, true)
|
||||||
|
|
||||||
if !args.Intention.CanWrite(authz) {
|
if !args.Intention.CanWrite(authz) {
|
||||||
sn := args.Intention.SourceServiceName()
|
sn := args.Intention.SourceServiceName()
|
||||||
|
@ -389,7 +389,7 @@ func (s *Intention) computeApplyChangesDelete(
|
||||||
entMeta *structs.EnterpriseMeta,
|
entMeta *structs.EnterpriseMeta,
|
||||||
args *structs.IntentionRequest,
|
args *structs.IntentionRequest,
|
||||||
) (*structs.IntentionMutation, error) {
|
) (*structs.IntentionMutation, error) {
|
||||||
args.Intention.DefaultNamespaces(entMeta)
|
args.Intention.FillPartitionAndNamespace(entMeta, true)
|
||||||
|
|
||||||
if !args.Intention.CanWrite(authz) {
|
if !args.Intention.CanWrite(authz) {
|
||||||
sn := args.Intention.SourceServiceName()
|
sn := args.Intention.SourceServiceName()
|
||||||
|
@ -753,9 +753,15 @@ func (s *Intention) aclAccessorID(secretID string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Intention) validateEnterpriseIntention(ixn *structs.Intention) error {
|
func (s *Intention) validateEnterpriseIntention(ixn *structs.Intention) error {
|
||||||
|
if err := s.srv.validateEnterpriseIntentionPartition(ixn.SourcePartition); err != nil {
|
||||||
|
return fmt.Errorf("Invalid source partition %q: %v", ixn.SourcePartition, err)
|
||||||
|
}
|
||||||
if err := s.srv.validateEnterpriseIntentionNamespace(ixn.SourceNS, true); err != nil {
|
if err := s.srv.validateEnterpriseIntentionNamespace(ixn.SourceNS, true); err != nil {
|
||||||
return fmt.Errorf("Invalid source namespace %q: %v", ixn.SourceNS, err)
|
return fmt.Errorf("Invalid source namespace %q: %v", ixn.SourceNS, err)
|
||||||
}
|
}
|
||||||
|
if err := s.srv.validateEnterpriseIntentionPartition(ixn.DestinationPartition); err != nil {
|
||||||
|
return fmt.Errorf("Invalid destination partition %q: %v", ixn.DestinationPartition, err)
|
||||||
|
}
|
||||||
if err := s.srv.validateEnterpriseIntentionNamespace(ixn.DestinationNS, true); err != nil {
|
if err := s.srv.validateEnterpriseIntentionNamespace(ixn.DestinationNS, true); err != nil {
|
||||||
return fmt.Errorf("Invalid destination namespace %q: %v", ixn.DestinationNS, err)
|
return fmt.Errorf("Invalid destination namespace %q: %v", ixn.DestinationNS, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,6 +74,8 @@ func TestIntentionApply_new(t *testing.T) {
|
||||||
actual.Hash = ixn.Intention.Hash
|
actual.Hash = ixn.Intention.Hash
|
||||||
//nolint:staticcheck
|
//nolint:staticcheck
|
||||||
ixn.Intention.UpdatePrecedence()
|
ixn.Intention.UpdatePrecedence()
|
||||||
|
// Partition fields will be normalized on Intention.Get
|
||||||
|
ixn.Intention.NormalizePartitionFields()
|
||||||
require.Equal(t, ixn.Intention, actual)
|
require.Equal(t, ixn.Intention, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +99,7 @@ func TestIntentionApply_new(t *testing.T) {
|
||||||
|
|
||||||
var reply string
|
var reply string
|
||||||
err := msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn2, &reply)
|
err := msgpackrpc.CallWithCodec(codec, "Intention.Apply", &ixn2, &reply)
|
||||||
testutil.RequireErrorContains(t, err, "Cannot modify DestinationNS or DestinationName for an intention once it exists.")
|
testutil.RequireErrorContains(t, err, "Cannot modify Destination partition/namespace/name for an intention once it exists.")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,6 +267,8 @@ func TestIntentionApply_updateGood(t *testing.T) {
|
||||||
actual.Hash = ixn.Intention.Hash
|
actual.Hash = ixn.Intention.Hash
|
||||||
//nolint:staticcheck
|
//nolint:staticcheck
|
||||||
ixn.Intention.UpdatePrecedence()
|
ixn.Intention.UpdatePrecedence()
|
||||||
|
// Partition fields will be normalized on Intention.Get
|
||||||
|
ixn.Intention.NormalizePartitionFields()
|
||||||
require.Equal(t, ixn.Intention, actual)
|
require.Equal(t, ixn.Intention, actual)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,9 @@ func (s *Server) startIntentionConfigEntryMigration(ctx context.Context) error {
|
||||||
// datacenter is composed entirely of compatible servers and there are
|
// datacenter is composed entirely of compatible servers and there are
|
||||||
// no more legacy intentions.
|
// no more legacy intentions.
|
||||||
if s.DatacenterSupportsIntentionsAsConfigEntries() {
|
if s.DatacenterSupportsIntentionsAsConfigEntries() {
|
||||||
|
// NOTE: we only have to migrate legacy intentions from the default
|
||||||
|
// partition because partitions didn't exist when legacy intentions
|
||||||
|
// were canonical
|
||||||
_, ixns, err := s.fsm.State().LegacyIntentions(nil, structs.WildcardEnterpriseMetaInDefaultPartition())
|
_, ixns, err := s.fsm.State().LegacyIntentions(nil, structs.WildcardEnterpriseMetaInDefaultPartition())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -88,6 +91,9 @@ func (s *Server) legacyIntentionMigration(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
state := s.fsm.State()
|
state := s.fsm.State()
|
||||||
|
// NOTE: we only have to migrate legacy intentions from the default
|
||||||
|
// partition because partitions didn't exist when legacy intentions
|
||||||
|
// were canonical
|
||||||
_, ixns, err := state.LegacyIntentions(nil, structs.WildcardEnterpriseMetaInDefaultPartition())
|
_, ixns, err := state.LegacyIntentions(nil, structs.WildcardEnterpriseMetaInDefaultPartition())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -128,7 +128,7 @@ func configIntentionsListTxn(tx ReadTxn, ws memdb.WatchSet, entMeta *structs.Ent
|
||||||
|
|
||||||
idx := maxIndexTxn(tx, tableConfigEntries)
|
idx := maxIndexTxn(tx, tableConfigEntries)
|
||||||
|
|
||||||
iter, err := getConfigEntryKindsWithTxn(tx, structs.ServiceIntentions, structs.WildcardEnterpriseMetaInDefaultPartition())
|
iter, err := getConfigEntryKindsWithTxn(tx, structs.ServiceIntentions, entMeta.WildcardEnterpriseMetaForPartition())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, false, fmt.Errorf("failed config entry lookup: %s", err)
|
return 0, nil, false, fmt.Errorf("failed config entry lookup: %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,12 @@ package state
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/hashicorp/consul/agent/connect"
|
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/hashicorp/go-memdb"
|
"github.com/hashicorp/go-memdb"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/acl"
|
"github.com/hashicorp/consul/acl"
|
||||||
|
"github.com/hashicorp/consul/agent/connect"
|
||||||
"github.com/hashicorp/consul/agent/structs"
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1008,7 +1008,7 @@ func (s *Store) intentionTopologyTxn(tx ReadTxn, ws memdb.WatchSet,
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}, structs.WildcardEnterpriseMetaInDefaultPartition())
|
}, target.WildcardEnterpriseMetaForPartition())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return index, nil, fmt.Errorf("failed to fetch catalog service list: %v", err)
|
return index, nil, fmt.Errorf("failed to fetch catalog service list: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,6 +155,8 @@ func TestStore_IntentionSetGet_basic(t *testing.T) {
|
||||||
expected.UpdatePrecedence()
|
expected.UpdatePrecedence()
|
||||||
//nolint:staticcheck
|
//nolint:staticcheck
|
||||||
expected.SetHash()
|
expected.SetHash()
|
||||||
|
|
||||||
|
expected.NormalizePartitionFields()
|
||||||
}
|
}
|
||||||
require.True(t, watchFired(ws), "watch fired")
|
require.True(t, watchFired(ws), "watch fired")
|
||||||
|
|
||||||
|
@ -1083,7 +1085,7 @@ func TestStore_IntentionsList(t *testing.T) {
|
||||||
require.Equal(t, lastIndex, idx)
|
require.Equal(t, lastIndex, idx)
|
||||||
|
|
||||||
testIntention := func(src, dst string) *structs.Intention {
|
testIntention := func(src, dst string) *structs.Intention {
|
||||||
return &structs.Intention{
|
ret := &structs.Intention{
|
||||||
ID: testUUID(),
|
ID: testUUID(),
|
||||||
SourceNS: "default",
|
SourceNS: "default",
|
||||||
SourceName: src,
|
SourceName: src,
|
||||||
|
@ -1095,6 +1097,10 @@ func TestStore_IntentionsList(t *testing.T) {
|
||||||
CreatedAt: testTimeA,
|
CreatedAt: testTimeA,
|
||||||
UpdatedAt: testTimeA,
|
UpdatedAt: testTimeA,
|
||||||
}
|
}
|
||||||
|
if !legacy {
|
||||||
|
ret.NormalizePartitionFields()
|
||||||
|
}
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
testConfigEntry := func(dst string, srcs ...string) *structs.ServiceIntentionsConfigEntry {
|
testConfigEntry := func(dst string, srcs ...string) *structs.ServiceIntentionsConfigEntry {
|
||||||
|
|
|
@ -21,6 +21,18 @@ func (s *HTTPHandlers) parseEntMeta(req *http.Request, entMeta *structs.Enterpri
|
||||||
return s.parseEntMetaPartition(req, entMeta)
|
return s.parseEntMetaPartition(req, entMeta)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *HTTPHandlers) validateEnterpriseIntentionPartition(logName, partition string) error {
|
||||||
|
if partition == "" {
|
||||||
|
return nil
|
||||||
|
} else if strings.ToLower(partition) == "default" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// No special handling for wildcard namespaces as they are pointless in OSS.
|
||||||
|
|
||||||
|
return BadRequestError{Reason: "Invalid " + logName + "(" + partition + ")" + ": Partitions is a Consul Enterprise feature"}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *HTTPHandlers) validateEnterpriseIntentionNamespace(logName, ns string, _ bool) error {
|
func (s *HTTPHandlers) validateEnterpriseIntentionNamespace(logName, ns string, _ bool) error {
|
||||||
if ns == "" {
|
if ns == "" {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -54,6 +54,9 @@ func (s *HTTPHandlers) IntentionCreate(resp http.ResponseWriter, req *http.Reque
|
||||||
if err := s.parseEntMetaNoWildcard(req, &entMeta); err != nil {
|
if err := s.parseEntMetaNoWildcard(req, &entMeta); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if entMeta.PartitionOrDefault() != structs.PartitionOrDefault("") {
|
||||||
|
return nil, BadRequestError{Reason: "Cannot use a partition with this endpoint"}
|
||||||
|
}
|
||||||
|
|
||||||
args := structs.IntentionRequest{
|
args := structs.IntentionRequest{
|
||||||
Op: structs.IntentionOpCreate,
|
Op: structs.IntentionOpCreate,
|
||||||
|
@ -64,7 +67,8 @@ func (s *HTTPHandlers) IntentionCreate(resp http.ResponseWriter, req *http.Reque
|
||||||
return nil, fmt.Errorf("Failed to decode request body: %s", err)
|
return nil, fmt.Errorf("Failed to decode request body: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
args.Intention.FillNonDefaultNamespaces(&entMeta)
|
// TODO(partitions): reject non-empty/non-default partitions from the decoded body
|
||||||
|
args.Intention.FillPartitionAndNamespace(&entMeta, false)
|
||||||
|
|
||||||
if err := s.validateEnterpriseIntention(args.Intention); err != nil {
|
if err := s.validateEnterpriseIntention(args.Intention); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -79,12 +83,19 @@ func (s *HTTPHandlers) IntentionCreate(resp http.ResponseWriter, req *http.Reque
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *HTTPHandlers) validateEnterpriseIntention(ixn *structs.Intention) error {
|
func (s *HTTPHandlers) validateEnterpriseIntention(ixn *structs.Intention) error {
|
||||||
|
if err := s.validateEnterpriseIntentionPartition("SourcePartition", ixn.SourcePartition); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.validateEnterpriseIntentionPartition("DestinationPartition", ixn.DestinationPartition); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := s.validateEnterpriseIntentionNamespace("SourceNS", ixn.SourceNS, true); err != nil {
|
if err := s.validateEnterpriseIntentionNamespace("SourceNS", ixn.SourceNS, true); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := s.validateEnterpriseIntentionNamespace("DestinationNS", ixn.DestinationNS, true); err != nil {
|
if err := s.validateEnterpriseIntentionNamespace("DestinationNS", ixn.DestinationNS, true); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,6 +142,7 @@ func (s *HTTPHandlers) IntentionMatch(resp http.ResponseWriter, req *http.Reques
|
||||||
}
|
}
|
||||||
|
|
||||||
args.Match.Entries[i] = structs.IntentionMatchEntry{
|
args.Match.Entries[i] = structs.IntentionMatchEntry{
|
||||||
|
Partition: entMeta.PartitionOrEmpty(),
|
||||||
Namespace: ns,
|
Namespace: ns,
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
|
@ -214,10 +226,12 @@ func (s *HTTPHandlers) IntentionCheck(resp http.ResponseWriter, req *http.Reques
|
||||||
// We parse them the same way as matches to extract namespace/name
|
// We parse them the same way as matches to extract namespace/name
|
||||||
args.Check.SourceName = source[0]
|
args.Check.SourceName = source[0]
|
||||||
if args.Check.SourceType == structs.IntentionSourceConsul {
|
if args.Check.SourceType == structs.IntentionSourceConsul {
|
||||||
|
// TODO(partitions): this func should return partition
|
||||||
ns, name, err := parseIntentionStringComponent(source[0], &entMeta)
|
ns, name, err := parseIntentionStringComponent(source[0], &entMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("source %q is invalid: %s", source[0], err)
|
return nil, fmt.Errorf("source %q is invalid: %s", source[0], err)
|
||||||
}
|
}
|
||||||
|
args.Check.SourcePartition = entMeta.PartitionOrEmpty()
|
||||||
args.Check.SourceNS = ns
|
args.Check.SourceNS = ns
|
||||||
args.Check.SourceName = name
|
args.Check.SourceName = name
|
||||||
}
|
}
|
||||||
|
@ -227,6 +241,7 @@ func (s *HTTPHandlers) IntentionCheck(resp http.ResponseWriter, req *http.Reques
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("destination %q is invalid: %s", destination[0], err)
|
return nil, fmt.Errorf("destination %q is invalid: %s", destination[0], err)
|
||||||
}
|
}
|
||||||
|
args.Check.DestinationPartition = entMeta.PartitionOrEmpty()
|
||||||
args.Check.DestinationNS = ns
|
args.Check.DestinationNS = ns
|
||||||
args.Check.DestinationName = name
|
args.Check.DestinationName = name
|
||||||
|
|
||||||
|
@ -269,6 +284,7 @@ func (s *HTTPHandlers) IntentionGetExact(resp http.ResponseWriter, req *http.Req
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("source %q is invalid: %s", source[0], err)
|
return nil, fmt.Errorf("source %q is invalid: %s", source[0], err)
|
||||||
}
|
}
|
||||||
|
args.Exact.SourcePartition = entMeta.PartitionOrEmpty()
|
||||||
args.Exact.SourceNS = ns
|
args.Exact.SourceNS = ns
|
||||||
args.Exact.SourceName = name
|
args.Exact.SourceName = name
|
||||||
}
|
}
|
||||||
|
@ -278,6 +294,7 @@ func (s *HTTPHandlers) IntentionGetExact(resp http.ResponseWriter, req *http.Req
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("destination %q is invalid: %s", destination[0], err)
|
return nil, fmt.Errorf("destination %q is invalid: %s", destination[0], err)
|
||||||
}
|
}
|
||||||
|
args.Exact.DestinationPartition = entMeta.PartitionOrEmpty()
|
||||||
args.Exact.DestinationNS = ns
|
args.Exact.DestinationNS = ns
|
||||||
args.Exact.DestinationName = name
|
args.Exact.DestinationName = name
|
||||||
}
|
}
|
||||||
|
@ -394,6 +411,9 @@ func (s *HTTPHandlers) IntentionSpecificUpdate(id string, resp http.ResponseWrit
|
||||||
if err := s.parseEntMetaNoWildcard(req, &entMeta); err != nil {
|
if err := s.parseEntMetaNoWildcard(req, &entMeta); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if entMeta.PartitionOrDefault() != structs.PartitionOrDefault("") {
|
||||||
|
return nil, BadRequestError{Reason: "Cannot use a partition with this endpoint"}
|
||||||
|
}
|
||||||
|
|
||||||
args := structs.IntentionRequest{
|
args := structs.IntentionRequest{
|
||||||
Op: structs.IntentionOpUpdate,
|
Op: structs.IntentionOpUpdate,
|
||||||
|
@ -404,7 +424,7 @@ func (s *HTTPHandlers) IntentionSpecificUpdate(id string, resp http.ResponseWrit
|
||||||
return nil, BadRequestError{Reason: fmt.Sprintf("Request decode failed: %v", err)}
|
return nil, BadRequestError{Reason: fmt.Sprintf("Request decode failed: %v", err)}
|
||||||
}
|
}
|
||||||
|
|
||||||
args.Intention.FillNonDefaultNamespaces(&entMeta)
|
args.Intention.FillPartitionAndNamespace(&entMeta, false)
|
||||||
|
|
||||||
// Use the ID from the URL
|
// Use the ID from the URL
|
||||||
args.Intention.ID = id
|
args.Intention.ID = id
|
||||||
|
@ -444,12 +464,14 @@ func (s *HTTPHandlers) IntentionPutExact(resp http.ResponseWriter, req *http.Req
|
||||||
args.Intention.ID = ""
|
args.Intention.ID = ""
|
||||||
|
|
||||||
// Use the intention identity from the URL.
|
// Use the intention identity from the URL.
|
||||||
|
args.Intention.SourcePartition = exact.SourcePartition
|
||||||
args.Intention.SourceNS = exact.SourceNS
|
args.Intention.SourceNS = exact.SourceNS
|
||||||
args.Intention.SourceName = exact.SourceName
|
args.Intention.SourceName = exact.SourceName
|
||||||
|
args.Intention.DestinationPartition = exact.DestinationPartition
|
||||||
args.Intention.DestinationNS = exact.DestinationNS
|
args.Intention.DestinationNS = exact.DestinationNS
|
||||||
args.Intention.DestinationName = exact.DestinationName
|
args.Intention.DestinationName = exact.DestinationName
|
||||||
|
|
||||||
args.Intention.FillNonDefaultNamespaces(&entMeta)
|
args.Intention.FillPartitionAndNamespace(&entMeta, false)
|
||||||
|
|
||||||
var ignored string
|
var ignored string
|
||||||
if err := s.agent.RPC("Intention.Apply", &args, &ignored); err != nil {
|
if err := s.agent.RPC("Intention.Apply", &args, &ignored); err != nil {
|
||||||
|
@ -494,10 +516,12 @@ func (s *HTTPHandlers) IntentionDeleteExact(resp http.ResponseWriter, req *http.
|
||||||
Op: structs.IntentionOpDelete,
|
Op: structs.IntentionOpDelete,
|
||||||
Intention: &structs.Intention{
|
Intention: &structs.Intention{
|
||||||
// NOTE: ID is explicitly empty here
|
// NOTE: ID is explicitly empty here
|
||||||
SourceNS: exact.SourceNS,
|
SourcePartition: exact.SourcePartition,
|
||||||
SourceName: exact.SourceName,
|
SourceNS: exact.SourceNS,
|
||||||
DestinationNS: exact.DestinationNS,
|
SourceName: exact.SourceName,
|
||||||
DestinationName: exact.DestinationName,
|
DestinationPartition: exact.DestinationPartition,
|
||||||
|
DestinationNS: exact.DestinationNS,
|
||||||
|
DestinationName: exact.DestinationName,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
s.parseDC(req, &args.Datacenter)
|
s.parseDC(req, &args.Datacenter)
|
||||||
|
@ -533,6 +557,7 @@ func parseIntentionQueryExact(req *http.Request, entMeta *structs.EnterpriseMeta
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("source %q is invalid: %s", source[0], err)
|
return nil, fmt.Errorf("source %q is invalid: %s", source[0], err)
|
||||||
}
|
}
|
||||||
|
exact.SourcePartition = entMeta.PartitionOrEmpty()
|
||||||
exact.SourceNS = ns
|
exact.SourceNS = ns
|
||||||
exact.SourceName = name
|
exact.SourceName = name
|
||||||
}
|
}
|
||||||
|
@ -542,6 +567,7 @@ func parseIntentionQueryExact(req *http.Request, entMeta *structs.EnterpriseMeta
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("destination %q is invalid: %s", destination[0], err)
|
return nil, fmt.Errorf("destination %q is invalid: %s", destination[0], err)
|
||||||
}
|
}
|
||||||
|
exact.DestinationPartition = entMeta.PartitionOrEmpty()
|
||||||
exact.DestinationNS = ns
|
exact.DestinationNS = ns
|
||||||
exact.DestinationName = name
|
exact.DestinationName = name
|
||||||
}
|
}
|
||||||
|
@ -549,6 +575,7 @@ func parseIntentionQueryExact(req *http.Request, entMeta *structs.EnterpriseMeta
|
||||||
return &exact, nil
|
return &exact, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(partitions): update to handle partitions
|
||||||
func parseIntentionStringComponent(input string, entMeta *structs.EnterpriseMeta) (string, string, error) {
|
func parseIntentionStringComponent(input string, entMeta *structs.EnterpriseMeta) (string, string, error) {
|
||||||
// Get the index to the '/'. If it doesn't exist, we have just a name
|
// Get the index to the '/'. If it doesn't exist, we have just a name
|
||||||
// so just set that and return.
|
// so just set that and return.
|
||||||
|
|
|
@ -6,11 +6,12 @@ import (
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/agent/structs"
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
"github.com/hashicorp/consul/sdk/testutil"
|
"github.com/hashicorp/consul/sdk/testutil"
|
||||||
"github.com/hashicorp/consul/testrpc"
|
"github.com/hashicorp/consul/testrpc"
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIntentionList(t *testing.T) {
|
func TestIntentionList(t *testing.T) {
|
||||||
|
|
|
@ -169,10 +169,6 @@ func (e *ServiceConfigEntry) Validate() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
validationErr = multierror.Append(validationErr, fmt.Errorf("error in upstream override for %s: %v", override.ServiceName(), err))
|
validationErr = multierror.Append(validationErr, fmt.Errorf("error in upstream override for %s: %v", override.ServiceName(), err))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := validateInnerEnterpriseMeta(&override.EnterpriseMeta, &e.EnterpriseMeta); err != nil {
|
|
||||||
validationErr = multierror.Append(validationErr, fmt.Errorf("error in upstream override for %s: %v", override.ServiceName(), err))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.UpstreamConfig.Defaults != nil {
|
if e.UpstreamConfig.Defaults != nil {
|
||||||
|
|
|
@ -119,19 +119,22 @@ func (e *ServiceIntentionsConfigEntry) ToIntention(src *SourceIntention) *Intent
|
||||||
}
|
}
|
||||||
|
|
||||||
ixn := &Intention{
|
ixn := &Intention{
|
||||||
ID: src.LegacyID,
|
ID: src.LegacyID,
|
||||||
Description: src.Description,
|
Description: src.Description,
|
||||||
SourceNS: src.NamespaceOrDefault(),
|
SourcePartition: src.PartitionOrEmpty(),
|
||||||
SourceName: src.Name,
|
SourceNS: src.NamespaceOrDefault(),
|
||||||
SourceType: src.Type,
|
SourceName: src.Name,
|
||||||
Action: src.Action,
|
SourceType: src.Type,
|
||||||
Permissions: src.Permissions,
|
Action: src.Action,
|
||||||
Meta: meta,
|
Permissions: src.Permissions,
|
||||||
Precedence: src.Precedence,
|
Meta: meta,
|
||||||
DestinationNS: e.NamespaceOrDefault(),
|
Precedence: src.Precedence,
|
||||||
DestinationName: e.Name,
|
DestinationPartition: e.PartitionOrEmpty(),
|
||||||
RaftIndex: e.RaftIndex,
|
DestinationNS: e.NamespaceOrDefault(),
|
||||||
|
DestinationName: e.Name,
|
||||||
|
RaftIndex: e.RaftIndex,
|
||||||
}
|
}
|
||||||
|
|
||||||
if src.LegacyCreateTime != nil {
|
if src.LegacyCreateTime != nil {
|
||||||
ixn.CreatedAt = *src.LegacyCreateTime
|
ixn.CreatedAt = *src.LegacyCreateTime
|
||||||
}
|
}
|
||||||
|
@ -764,6 +767,9 @@ func validateIntentionWildcards(name string, entMeta *EnterpriseMeta) error {
|
||||||
return fmt.Errorf("Name: exact value cannot follow wildcard namespace")
|
return fmt.Errorf("Name: exact value cannot follow wildcard namespace")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if strings.Contains(entMeta.PartitionOrDefault(), WildcardSpecifier) {
|
||||||
|
return fmt.Errorf("Partition: cannot use wildcard '*' in partition")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -815,5 +821,23 @@ func MigrateIntentions(ixns Intentions) []*ServiceIntentionsConfigEntry {
|
||||||
for _, entry := range collated {
|
for _, entry := range collated {
|
||||||
out = append(out, entry)
|
out = append(out, entry)
|
||||||
}
|
}
|
||||||
|
sort.Slice(out, func(i, j int) bool {
|
||||||
|
a := out[i]
|
||||||
|
b := out[j]
|
||||||
|
|
||||||
|
if a.PartitionOrDefault() < b.PartitionOrDefault() {
|
||||||
|
return true
|
||||||
|
} else if a.PartitionOrDefault() > b.PartitionOrDefault() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.NamespaceOrDefault() < b.NamespaceOrDefault() {
|
||||||
|
return true
|
||||||
|
} else if a.NamespaceOrDefault() > b.NamespaceOrDefault() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return a.Name < b.Name
|
||||||
|
})
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
|
@ -1296,7 +1296,7 @@ func TestMigrateIntentions(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
anyTime := time.Now().UTC()
|
anyTime := time.Now().UTC()
|
||||||
entMeta := NodeEnterpriseMetaInDefaultPartition()
|
entMeta := DefaultEnterpriseMetaInDefaultPartition()
|
||||||
|
|
||||||
cases := map[string]testcase{
|
cases := map[string]testcase{
|
||||||
"nil": {},
|
"nil": {},
|
||||||
|
@ -1487,7 +1487,7 @@ func TestMigrateIntentions(t *testing.T) {
|
||||||
tc := tc
|
tc := tc
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
got := MigrateIntentions(tc.in)
|
got := MigrateIntentions(tc.in)
|
||||||
require.ElementsMatch(t, tc.expect, got)
|
require.Equal(t, tc.expect, got)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2418,8 +2418,6 @@ func testConfigEntryNormalizeAndValidate(t *testing.T, cases map[string]configEn
|
||||||
|
|
||||||
err = tc.entry.Validate()
|
err = tc.entry.Validate()
|
||||||
if tc.validateErr != "" {
|
if tc.validateErr != "" {
|
||||||
// require.Error(t, err)
|
|
||||||
// require.Contains(t, err.Error(), tc.validateErr)
|
|
||||||
testutil.RequireErrorContains(t, err, tc.validateErr)
|
testutil.RequireErrorContains(t, err, tc.validateErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,11 @@ type Intention struct {
|
||||||
SourceNS, SourceName string
|
SourceNS, SourceName string
|
||||||
DestinationNS, DestinationName string
|
DestinationNS, DestinationName string
|
||||||
|
|
||||||
|
// SourcePartition and DestinationPartition cannot be wildcards "*" and
|
||||||
|
// are not compatible with legacy intentions.
|
||||||
|
SourcePartition string `json:",omitempty"`
|
||||||
|
DestinationPartition string `json:",omitempty"`
|
||||||
|
|
||||||
// SourceType is the type of the value for the source.
|
// SourceType is the type of the value for the source.
|
||||||
SourceType IntentionSourceType
|
SourceType IntentionSourceType
|
||||||
|
|
||||||
|
@ -111,10 +116,12 @@ func (t *Intention) Clone() *Intention {
|
||||||
|
|
||||||
func (t *Intention) ToExact() *IntentionQueryExact {
|
func (t *Intention) ToExact() *IntentionQueryExact {
|
||||||
return &IntentionQueryExact{
|
return &IntentionQueryExact{
|
||||||
SourceNS: t.SourceNS,
|
SourcePartition: t.SourcePartition,
|
||||||
SourceName: t.SourceName,
|
SourceNS: t.SourceNS,
|
||||||
DestinationNS: t.DestinationNS,
|
SourceName: t.SourceName,
|
||||||
DestinationName: t.DestinationName,
|
DestinationPartition: t.DestinationPartition,
|
||||||
|
DestinationNS: t.DestinationNS,
|
||||||
|
DestinationName: t.DestinationName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,6 +386,16 @@ func (x *Intention) String() string {
|
||||||
idPart = "ID: " + x.ID + ", "
|
idPart = "ID: " + x.ID + ", "
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var srcPartitionPart string
|
||||||
|
if x.SourcePartition != "" {
|
||||||
|
srcPartitionPart = x.SourcePartition + "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
var dstPartitionPart string
|
||||||
|
if x.DestinationPartition != "" {
|
||||||
|
dstPartitionPart = x.DestinationPartition + "/"
|
||||||
|
}
|
||||||
|
|
||||||
var detailPart string
|
var detailPart string
|
||||||
if len(x.Permissions) > 0 {
|
if len(x.Permissions) > 0 {
|
||||||
detailPart = fmt.Sprintf("Permissions: %d", len(x.Permissions))
|
detailPart = fmt.Sprintf("Permissions: %d", len(x.Permissions))
|
||||||
|
@ -386,9 +403,9 @@ func (x *Intention) String() string {
|
||||||
detailPart = "Action: " + strings.ToUpper(string(x.Action))
|
detailPart = "Action: " + strings.ToUpper(string(x.Action))
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("%s/%s => %s/%s (%sPrecedence: %d, %s)",
|
return fmt.Sprintf("%s%s/%s => %s%s/%s (%sPrecedence: %d, %s)",
|
||||||
x.SourceNS, x.SourceName,
|
srcPartitionPart, x.SourceNS, x.SourceName,
|
||||||
x.DestinationNS, x.DestinationName,
|
dstPartitionPart, x.DestinationNS, x.DestinationName,
|
||||||
idPart,
|
idPart,
|
||||||
x.Precedence,
|
x.Precedence,
|
||||||
detailPart,
|
detailPart,
|
||||||
|
@ -625,6 +642,7 @@ type IntentionQueryMatch struct {
|
||||||
|
|
||||||
// IntentionMatchEntry is a single entry for matching an intention.
|
// IntentionMatchEntry is a single entry for matching an intention.
|
||||||
type IntentionMatchEntry struct {
|
type IntentionMatchEntry struct {
|
||||||
|
Partition string `json:",omitempty"`
|
||||||
Namespace string
|
Namespace string
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
@ -637,6 +655,10 @@ type IntentionQueryCheck struct {
|
||||||
SourceNS, SourceName string
|
SourceNS, SourceName string
|
||||||
DestinationNS, DestinationName string
|
DestinationNS, DestinationName string
|
||||||
|
|
||||||
|
// TODO(partitions): check query works with partitions
|
||||||
|
SourcePartition string `json:",omitempty"`
|
||||||
|
DestinationPartition string `json:",omitempty"`
|
||||||
|
|
||||||
// SourceType is the type of the value for the source.
|
// SourceType is the type of the value for the source.
|
||||||
SourceType IntentionSourceType
|
SourceType IntentionSourceType
|
||||||
}
|
}
|
||||||
|
@ -673,9 +695,13 @@ type IntentionDecisionSummary struct {
|
||||||
type IntentionQueryExact struct {
|
type IntentionQueryExact struct {
|
||||||
SourceNS, SourceName string
|
SourceNS, SourceName string
|
||||||
DestinationNS, DestinationName string
|
DestinationNS, DestinationName string
|
||||||
|
|
||||||
|
// TODO(partitions): check query works with partitions
|
||||||
|
SourcePartition string `json:",omitempty"`
|
||||||
|
DestinationPartition string `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate is used to ensure all 4 parameters are specified.
|
// Validate is used to ensure all 4 required parameters are specified.
|
||||||
func (q *IntentionQueryExact) Validate() error {
|
func (q *IntentionQueryExact) Validate() error {
|
||||||
var err error
|
var err error
|
||||||
if q.SourceNS == "" {
|
if q.SourceNS == "" {
|
||||||
|
@ -721,18 +747,24 @@ func (s IntentionPrecedenceSorter) Less(i, j int) bool {
|
||||||
return a.Precedence > b.Precedence
|
return a.Precedence > b.Precedence
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tie break on lexicographic order of the 4-tuple in canonical form (SrcNS,
|
// Tie break on lexicographic order of the tuple in canonical form (SrcPxn,
|
||||||
// Src, DstNS, Dst). This is arbitrary but it keeps sorting deterministic
|
// SrcNS, Src, DstPxn, DstNS, Dst). This is arbitrary but it keeps sorting
|
||||||
// which is a nice property for consistency. It is arguably open to abuse if
|
// deterministic which is a nice property for consistency. It is arguably
|
||||||
// implementations rely on this however by definition the order among
|
// open to abuse if implementations rely on this however by definition the
|
||||||
// same-precedence rules is arbitrary and doesn't affect whether an allow or
|
// order among same-precedence rules is arbitrary and doesn't affect whether
|
||||||
// deny rule is acted on since all applicable rules are checked.
|
// an allow or deny rule is acted on since all applicable rules are checked.
|
||||||
|
if a.SourcePartition != b.SourcePartition {
|
||||||
|
return a.SourcePartition < b.SourcePartition
|
||||||
|
}
|
||||||
if a.SourceNS != b.SourceNS {
|
if a.SourceNS != b.SourceNS {
|
||||||
return a.SourceNS < b.SourceNS
|
return a.SourceNS < b.SourceNS
|
||||||
}
|
}
|
||||||
if a.SourceName != b.SourceName {
|
if a.SourceName != b.SourceName {
|
||||||
return a.SourceName < b.SourceName
|
return a.SourceName < b.SourceName
|
||||||
}
|
}
|
||||||
|
if a.DestinationPartition != b.DestinationPartition {
|
||||||
|
return a.DestinationPartition < b.DestinationPartition
|
||||||
|
}
|
||||||
if a.DestinationNS != b.DestinationNS {
|
if a.DestinationNS != b.DestinationNS {
|
||||||
return a.DestinationNS < b.DestinationNS
|
return a.DestinationNS < b.DestinationNS
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,21 +47,35 @@ func (_ *IntentionQueryCheck) FillAuthzContext(_ *acl.AuthorizerContext) {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultNamespaces will populate both the SourceNS and DestinationNS fields
|
// FillPartitionAndNamespace will fill in empty source and destination partition/namespaces.
|
||||||
// if they are empty with the proper defaults.
|
// If fillDefault is true, all fields are defaulted when the given enterprise meta does not
|
||||||
func (ixn *Intention) DefaultNamespaces(_ *EnterpriseMeta) {
|
// specify them.
|
||||||
// Until we support namespaces, we force all namespaces to be default
|
//
|
||||||
|
// fillDefault MUST be true on servers to ensure that all fields are populated on writes.
|
||||||
|
// fillDefault MUST be false on clients so that servers can correctly fill in the
|
||||||
|
// namespace/partition of the ACL token.
|
||||||
|
func (ixn *Intention) FillPartitionAndNamespace(entMeta *EnterpriseMeta, fillDefault bool) {
|
||||||
|
if ixn == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var ns = entMeta.NamespaceOrEmpty()
|
||||||
|
if fillDefault {
|
||||||
|
if ns == "" {
|
||||||
|
ns = IntentionDefaultNamespace
|
||||||
|
}
|
||||||
|
}
|
||||||
if ixn.SourceNS == "" {
|
if ixn.SourceNS == "" {
|
||||||
ixn.SourceNS = IntentionDefaultNamespace
|
ixn.SourceNS = ns
|
||||||
}
|
}
|
||||||
if ixn.DestinationNS == "" {
|
if ixn.DestinationNS == "" {
|
||||||
ixn.DestinationNS = IntentionDefaultNamespace
|
ixn.DestinationNS = ns
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ixn.SourcePartition = ""
|
||||||
|
ixn.DestinationPartition = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// FillNonDefaultNamespaces will populate the SourceNS and DestinationNS fields
|
func (ixn *Intention) NormalizePartitionFields() {
|
||||||
// if they are empty with the proper defaults, but only if the proper defaults
|
ixn.SourcePartition = ""
|
||||||
// are themselves not "default".
|
ixn.DestinationPartition = ""
|
||||||
func (ixn *Intention) FillNonDefaultNamespaces(_ *EnterpriseMeta) {
|
|
||||||
// do nothing
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -369,6 +369,11 @@ func TestIntention_String(t *testing.T) {
|
||||||
|
|
||||||
testID := generateUUID()
|
testID := generateUUID()
|
||||||
|
|
||||||
|
partitionPrefix := DefaultEnterpriseMetaInDefaultPartition().PartitionOrEmpty()
|
||||||
|
if partitionPrefix != "" {
|
||||||
|
partitionPrefix += "/"
|
||||||
|
}
|
||||||
|
|
||||||
cases := map[string]testcase{
|
cases := map[string]testcase{
|
||||||
"legacy allow": {
|
"legacy allow": {
|
||||||
&Intention{
|
&Intention{
|
||||||
|
@ -377,7 +382,7 @@ func TestIntention_String(t *testing.T) {
|
||||||
DestinationName: "bar",
|
DestinationName: "bar",
|
||||||
Action: IntentionActionAllow,
|
Action: IntentionActionAllow,
|
||||||
},
|
},
|
||||||
`default/foo => default/bar (ID: ` + testID + `, Precedence: 9, Action: ALLOW)`,
|
partitionPrefix + `default/foo => ` + partitionPrefix + `default/bar (ID: ` + testID + `, Precedence: 9, Action: ALLOW)`,
|
||||||
},
|
},
|
||||||
"legacy deny": {
|
"legacy deny": {
|
||||||
&Intention{
|
&Intention{
|
||||||
|
@ -386,7 +391,7 @@ func TestIntention_String(t *testing.T) {
|
||||||
DestinationName: "bar",
|
DestinationName: "bar",
|
||||||
Action: IntentionActionDeny,
|
Action: IntentionActionDeny,
|
||||||
},
|
},
|
||||||
`default/foo => default/bar (ID: ` + testID + `, Precedence: 9, Action: DENY)`,
|
partitionPrefix + `default/foo => ` + partitionPrefix + `default/bar (ID: ` + testID + `, Precedence: 9, Action: DENY)`,
|
||||||
},
|
},
|
||||||
"L4 allow": {
|
"L4 allow": {
|
||||||
&Intention{
|
&Intention{
|
||||||
|
@ -394,7 +399,7 @@ func TestIntention_String(t *testing.T) {
|
||||||
DestinationName: "bar",
|
DestinationName: "bar",
|
||||||
Action: IntentionActionAllow,
|
Action: IntentionActionAllow,
|
||||||
},
|
},
|
||||||
`default/foo => default/bar (Precedence: 9, Action: ALLOW)`,
|
partitionPrefix + `default/foo => ` + partitionPrefix + `default/bar (Precedence: 9, Action: ALLOW)`,
|
||||||
},
|
},
|
||||||
"L4 deny": {
|
"L4 deny": {
|
||||||
&Intention{
|
&Intention{
|
||||||
|
@ -402,7 +407,7 @@ func TestIntention_String(t *testing.T) {
|
||||||
DestinationName: "bar",
|
DestinationName: "bar",
|
||||||
Action: IntentionActionDeny,
|
Action: IntentionActionDeny,
|
||||||
},
|
},
|
||||||
`default/foo => default/bar (Precedence: 9, Action: DENY)`,
|
partitionPrefix + `default/foo => ` + partitionPrefix + `default/bar (Precedence: 9, Action: DENY)`,
|
||||||
},
|
},
|
||||||
"L7 one perm": {
|
"L7 one perm": {
|
||||||
&Intention{
|
&Intention{
|
||||||
|
@ -417,7 +422,7 @@ func TestIntention_String(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
`default/foo => default/bar (Precedence: 9, Permissions: 1)`,
|
partitionPrefix + `default/foo => ` + partitionPrefix + `default/bar (Precedence: 9, Permissions: 1)`,
|
||||||
},
|
},
|
||||||
"L7 two perms": {
|
"L7 two perms": {
|
||||||
&Intention{
|
&Intention{
|
||||||
|
@ -438,14 +443,14 @@ func TestIntention_String(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
`default/foo => default/bar (Precedence: 9, Permissions: 2)`,
|
partitionPrefix + `default/foo => ` + partitionPrefix + `default/bar (Precedence: 9, Permissions: 2)`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, tc := range cases {
|
for name, tc := range cases {
|
||||||
tc := tc
|
tc := tc
|
||||||
// Add a bunch of required fields.
|
// Add a bunch of required fields.
|
||||||
tc.ixn.DefaultNamespaces(DefaultEnterpriseMetaInDefaultPartition())
|
tc.ixn.FillPartitionAndNamespace(DefaultEnterpriseMetaInDefaultPartition(), true)
|
||||||
tc.ixn.UpdatePrecedence()
|
tc.ixn.UpdatePrecedence()
|
||||||
|
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
|
|
|
@ -652,6 +652,11 @@ var expectedFieldConfigIntention bexpr.FieldConfigurations = bexpr.FieldConfigur
|
||||||
CoerceFn: bexpr.CoerceString,
|
CoerceFn: bexpr.CoerceString,
|
||||||
SupportedOperations: []bexpr.MatchOperator{bexpr.MatchEqual, bexpr.MatchNotEqual, bexpr.MatchIn, bexpr.MatchNotIn, bexpr.MatchMatches, bexpr.MatchNotMatches},
|
SupportedOperations: []bexpr.MatchOperator{bexpr.MatchEqual, bexpr.MatchNotEqual, bexpr.MatchIn, bexpr.MatchNotIn, bexpr.MatchMatches, bexpr.MatchNotMatches},
|
||||||
},
|
},
|
||||||
|
"SourcePartition": &bexpr.FieldConfiguration{
|
||||||
|
StructFieldName: "SourcePartition",
|
||||||
|
CoerceFn: bexpr.CoerceString,
|
||||||
|
SupportedOperations: []bexpr.MatchOperator{bexpr.MatchEqual, bexpr.MatchNotEqual, bexpr.MatchIn, bexpr.MatchNotIn, bexpr.MatchMatches, bexpr.MatchNotMatches},
|
||||||
|
},
|
||||||
"SourceNS": &bexpr.FieldConfiguration{
|
"SourceNS": &bexpr.FieldConfiguration{
|
||||||
StructFieldName: "SourceNS",
|
StructFieldName: "SourceNS",
|
||||||
CoerceFn: bexpr.CoerceString,
|
CoerceFn: bexpr.CoerceString,
|
||||||
|
@ -662,6 +667,11 @@ var expectedFieldConfigIntention bexpr.FieldConfigurations = bexpr.FieldConfigur
|
||||||
CoerceFn: bexpr.CoerceString,
|
CoerceFn: bexpr.CoerceString,
|
||||||
SupportedOperations: []bexpr.MatchOperator{bexpr.MatchEqual, bexpr.MatchNotEqual, bexpr.MatchIn, bexpr.MatchNotIn, bexpr.MatchMatches, bexpr.MatchNotMatches},
|
SupportedOperations: []bexpr.MatchOperator{bexpr.MatchEqual, bexpr.MatchNotEqual, bexpr.MatchIn, bexpr.MatchNotIn, bexpr.MatchMatches, bexpr.MatchNotMatches},
|
||||||
},
|
},
|
||||||
|
"DestinationPartition": &bexpr.FieldConfiguration{
|
||||||
|
StructFieldName: "DestinationPartition",
|
||||||
|
CoerceFn: bexpr.CoerceString,
|
||||||
|
SupportedOperations: []bexpr.MatchOperator{bexpr.MatchEqual, bexpr.MatchNotEqual, bexpr.MatchIn, bexpr.MatchNotIn, bexpr.MatchMatches, bexpr.MatchNotMatches},
|
||||||
|
},
|
||||||
"DestinationNS": &bexpr.FieldConfiguration{
|
"DestinationNS": &bexpr.FieldConfiguration{
|
||||||
StructFieldName: "DestinationNS",
|
StructFieldName: "DestinationNS",
|
||||||
CoerceFn: bexpr.CoerceString,
|
CoerceFn: bexpr.CoerceString,
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
|
|
||||||
// TestIntention returns a valid, uninserted (no ID set) intention.
|
// TestIntention returns a valid, uninserted (no ID set) intention.
|
||||||
func TestIntention(t testing.T) *Intention {
|
func TestIntention(t testing.T) *Intention {
|
||||||
return &Intention{
|
ixn := &Intention{
|
||||||
SourceNS: IntentionDefaultNamespace,
|
SourceNS: IntentionDefaultNamespace,
|
||||||
SourceName: "api",
|
SourceName: "api",
|
||||||
DestinationNS: IntentionDefaultNamespace,
|
DestinationNS: IntentionDefaultNamespace,
|
||||||
|
@ -15,4 +15,6 @@ func TestIntention(t testing.T) *Intention {
|
||||||
SourceType: IntentionSourceConsul,
|
SourceType: IntentionSourceConsul,
|
||||||
Meta: map[string]string{},
|
Meta: map[string]string{},
|
||||||
}
|
}
|
||||||
|
ixn.NormalizePartitionFields()
|
||||||
|
return ixn
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue