Update assumptions around exported-service config

Given that the exported-services config entry can use wildcards, the
precedence for wildcards is handled as with intentions. The most exact
match is the match that applies for any given service. We do not take
the union of all that apply.

Another update that was made was to reflect that only one
exported-services config entry applies to any given service in a
partition. This is a pre-existing constraint that gets enforced by
the Normalize() method on that config entry type.
This commit is contained in:
freddygv 2022-05-23 10:16:39 -06:00
parent 6ef38eaea7
commit 073c9e3a91
6 changed files with 240 additions and 279 deletions

View File

@ -603,6 +603,10 @@ func validateProposedConfigEntryInServiceGraph(
wildcardEntMeta := kindName.WithWildcardNamespace() wildcardEntMeta := kindName.WithWildcardNamespace()
switch kindName.Kind { switch kindName.Kind {
case structs.ExportedServices, structs.MeshConfig:
// Exported services and mesh config do not influence discovery chains.
return nil
case structs.ProxyDefaults: case structs.ProxyDefaults:
// Check anything that has a discovery chain entry. In the future we could // Check anything that has a discovery chain entry. In the future we could
// somehow omit the ones that have a default protocol configured. // somehow omit the ones that have a default protocol configured.
@ -1414,52 +1418,6 @@ func configEntryWithOverridesTxn(
return configEntryTxn(tx, ws, kind, name, entMeta) return configEntryTxn(tx, ws, kind, name, entMeta)
} }
// getExportedServicesConfigEntriesTxn fetches exported-service config entries and
// filters their exported services to only those that match serviceName and entMeta.
// Because the resulting config entries may have had their exported services modified,
// they *should not* be used in subsequent writes.
func getExportedServiceConfigEntriesTxn(
tx ReadTxn,
ws memdb.WatchSet,
serviceName string,
entMeta *acl.EnterpriseMeta,
) (uint64, []*structs.ExportedServicesConfigEntry, error) {
var exportedServicesEntries []*structs.ExportedServicesConfigEntry
// slice of names to match config entries against
matchCandidates := getExportedServicesMatchServiceNames(serviceName, entMeta)
// matcher func generator for currying the matcher func over EnterpriseMeta values
// from the associated config entry
matchFunc := func(matchMeta *acl.EnterpriseMeta) func(structs.ExportedService) bool {
return func(exportedService structs.ExportedService) bool {
matchSvcName := structs.NewServiceName(exportedService.Name, matchMeta)
for _, candidate := range matchCandidates {
if candidate.Matches(matchSvcName) {
return true
}
}
return false
}
}
idx, entries, err := configEntriesByKindTxn(tx, ws, structs.ExportedServices, entMeta)
if err != nil {
return 0, nil, err
}
for _, entry := range entries {
esEntry, ok := entry.(*structs.ExportedServicesConfigEntry)
if !ok {
return 0, nil, fmt.Errorf("type %T is not a %s config entry", esEntry, structs.ExportedServices)
}
// get a copy of the config entry with Services filtered to match serviceName
newEntry := filterExportedServices(esEntry, matchFunc(entry.GetEnterpriseMeta()))
// the filter will return a new entry, so checking to see if its services is empty says that there
// were matches and that we should include it in the results
if len(newEntry.Services) > 0 {
exportedServicesEntries = append(exportedServicesEntries, newEntry)
}
}
return idx, exportedServicesEntries, nil
}
// protocolForService returns the service graph protocol associated to the // protocolForService returns the service graph protocol associated to the
// provided service, checking all relevant config entries. // provided service, checking all relevant config entries.
func protocolForService( func protocolForService(
@ -1502,23 +1460,6 @@ func protocolForService(
return maxIdx, chain.Protocol, nil return maxIdx, chain.Protocol, nil
} }
// filterExportedServices returns the slice of ExportedService that matc ffor matching service names
// returning a copy of entry with only the services that match one of the
// services in candidates.
func filterExportedServices(
entry *structs.ExportedServicesConfigEntry,
testFunc func(structs.ExportedService) bool,
) *structs.ExportedServicesConfigEntry {
newEntry := *entry
newEntry.Services = []structs.ExportedService{}
for _, ceSvc := range entry.Services {
if testFunc(ceSvc) {
newEntry.Services = append(newEntry.Services, ceSvc)
}
}
return &newEntry
}
func newConfigEntryQuery(c structs.ConfigEntry) configentry.KindName { func newConfigEntryQuery(c structs.ConfigEntry) configentry.KindName {
return configentry.NewKindName(c.GetKind(), c.GetName(), c.GetEnterpriseMeta()) return configentry.NewKindName(c.GetKind(), c.GetName(), c.GetEnterpriseMeta())
} }

View File

@ -40,120 +40,124 @@ func testIndexerTableConfigEntries() map[string]indexerTestCase {
} }
} }
func TestStore_ExportedServices(t *testing.T) { func TestStore_peersForService(t *testing.T) {
queryName := "foo"
type testCase struct { type testCase struct {
name string name string
write []structs.ConfigEntry write structs.ConfigEntry
query string expect []string
expect []*structs.ExportedServicesConfigEntry
} }
cases := []testCase{ cases := []testCase{
{ {
name: "empty everything", name: "empty everything",
write: []structs.ConfigEntry{}, expect: nil,
query: "foo",
expect: []*structs.ExportedServicesConfigEntry{},
}, },
{ {
name: "no matching exported services", name: "service is not exported",
write: []structs.ConfigEntry{ write: &structs.ExportedServicesConfigEntry{
&structs.ProxyConfigEntry{Name: "foo"}, Name: "default",
&structs.ProxyConfigEntry{Name: "bar"}, Services: []structs.ExportedService{
&structs.ExportedServicesConfigEntry{ {
Name: "baz", Name: "not-" + queryName,
Services: []structs.ExportedService{ Consumers: []structs.ServiceConsumer{
{Name: "baz"}, {
PeerName: "zip",
},
},
}, },
}, },
}, },
query: "foo", expect: nil,
expect: []*structs.ExportedServicesConfigEntry{},
}, },
{ {
name: "exact match service name", name: "wildcard name matches",
write: []structs.ConfigEntry{ write: &structs.ExportedServicesConfigEntry{
&structs.ExportedServicesConfigEntry{ Name: "default",
Name: "foo", Services: []structs.ExportedService{
Services: []structs.ExportedService{ {
{Name: "foo"}, Name: "not-" + queryName,
Consumers: []structs.ServiceConsumer{
{
PeerName: "zip",
},
},
}, },
}, {
&structs.ExportedServicesConfigEntry{ Name: structs.WildcardSpecifier,
Name: "bar", Consumers: []structs.ServiceConsumer{
Services: []structs.ExportedService{ {
{Name: "bar"}, PeerName: "bar",
}, },
}, {
}, PeerName: "baz",
query: "bar", },
expect: []*structs.ExportedServicesConfigEntry{ },
{
Name: "bar",
Services: []structs.ExportedService{
{Name: "bar"},
}, },
}, },
}, },
expect: []string{"bar", "baz"},
}, },
{ {
name: "wildcard match on service name", name: "exact name takes precedence over wildcard",
write: []structs.ConfigEntry{ write: &structs.ExportedServicesConfigEntry{
&structs.ExportedServicesConfigEntry{ Name: "default",
Name: "foo", Services: []structs.ExportedService{
Services: []structs.ExportedService{ {
{Name: "foo"}, Name: queryName,
Consumers: []structs.ServiceConsumer{
{
PeerName: "baz",
},
},
}, },
}, {
&structs.ExportedServicesConfigEntry{ Name: structs.WildcardSpecifier,
Name: "wildcard", Consumers: []structs.ServiceConsumer{
Services: []structs.ExportedService{ {
{Name: structs.WildcardSpecifier}, PeerName: "zip",
}, },
}, },
},
query: "foo",
expect: []*structs.ExportedServicesConfigEntry{
{
Name: "foo",
Services: []structs.ExportedService{
{Name: "foo"},
},
},
{
Name: "wildcard",
Services: []structs.ExportedService{
{Name: structs.WildcardSpecifier},
}, },
}, },
}, },
expect: []string{"baz"},
}, },
} }
for _, tc := range cases { for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
s := testStateStore(t) s := testStateStore(t)
var lastIdx uint64
// Write the entries. // Write the entry.
for idx, entry := range tc.write { if tc.write != nil {
require.NoError(t, s.EnsureConfigEntry(uint64(idx+1), entry)) require.NoError(t, tc.write.Normalize())
require.NoError(t, tc.write.Validate())
lastIdx++
require.NoError(t, s.EnsureConfigEntry(lastIdx, tc.write))
} }
// Read the entries back. // Read the entries back.
tx := s.db.ReadTxn() tx := s.db.ReadTxn()
defer tx.Abort() defer tx.Abort()
idx, entries, err := getExportedServiceConfigEntriesTxn(tx, nil, tc.query, acl.DefaultEnterpriseMeta())
idx, peers, err := peersForServiceTxn(tx, nil, queryName, acl.DefaultEnterpriseMeta())
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, uint64(len(tc.write)), idx)
// This is a little weird, but when there are no results, the index returned should be the max index for the
// config entries table so that the caller can watch for changes to it
if len(peers) == 0 {
require.Equal(t, maxIndexTxn(tx, tableConfigEntries), idx)
} else {
require.Equal(t, lastIdx, idx)
}
// Verify the result. // Verify the result.
require.Len(t, entries, len(tc.expect)) require.Len(t, peers, len(tc.expect))
for idx, got := range entries { require.Equal(t, tc.expect, peers)
// ignore raft fields
got.ModifyIndex = 0
got.CreateIndex = 0
require.Equal(t, tc.expect[idx], got)
}
}) })
} }
} }

View File

@ -439,35 +439,37 @@ func (s *Store) exportedServicesForPeerTxn(ws memdb.WatchSet, tx ReadTxn, peerin
// PeeringsForService returns the list of peerings that are associated with the service name provided in the query. // PeeringsForService returns the list of peerings that are associated with the service name provided in the query.
// This is used to configure connect proxies for a given service. The result is generated by querying for exported // This is used to configure connect proxies for a given service. The result is generated by querying for exported
// service config entries and filtering for those that match the given service. // service config entries and filtering for those that match the given service.
//
// TODO(peering): this implementation does all of the work on read to materialize this list of peerings, we should explore // TODO(peering): this implementation does all of the work on read to materialize this list of peerings, we should explore
// writing to a separate index that has service peerings prepared ahead of time should this become a performance bottleneck. // writing to a separate index that has service peerings prepared ahead of time should this become a performance bottleneck.
func (s *Store) PeeringsForService(ws memdb.WatchSet, serviceName string, entMeta acl.EnterpriseMeta) (uint64, []*pbpeering.Peering, error) { func (s *Store) PeeringsForService(ws memdb.WatchSet, serviceName string, entMeta acl.EnterpriseMeta) (uint64, []*pbpeering.Peering, error) {
tx := s.db.ReadTxn() tx := s.db.ReadTxn()
defer tx.Abort() defer tx.Abort()
// short-circuit if the service does not exist in the context of the query -- this prevents "leaking" services // Short-circuit if the service does not exist in the context of the query -- this prevents "leaking" services
// when there are wildcard rules in place. // when there are wildcard rules in place.
if svcIdx, svcExists, err := serviceExists(tx, ws, serviceName, &entMeta, ""); err != nil { if svcIdx, svcExists, err := serviceExists(tx, ws, serviceName, &entMeta, ""); err != nil {
return 0, nil, fmt.Errorf("failed to check if service exists: %w", err) return 0, nil, fmt.Errorf("failed to check if service exists: %w", err)
} else if !svcExists { } else if !svcExists {
// if the service does not exist, return the max index for the services table so caller can watch for changes // If the service does not exist, return the max index for the services table so caller can watch for changes.
return svcIdx, nil, nil return svcIdx, nil, nil
} }
// config entries must be defined in the default namespace, so we only need the partition here
meta := structs.DefaultEnterpriseMetaInPartition(entMeta.PartitionOrDefault()) // Return the idx of the config entry so the caller can watch for changes.
// return the idx of the config entry that was last modified so caller can watch for changes idx, peerNames, err := peersForServiceTxn(tx, ws, serviceName, &entMeta)
idx, peeredServices, err := readPeeredServicesFromConfigEntriesTxn(tx, ws, serviceName, meta)
if err != nil { if err != nil {
return 0, nil, fmt.Errorf("failed to read peered services for service name: %w", err) return 0, nil, fmt.Errorf("failed to read peers for service name %q: %w", serviceName, err)
} }
var peerings []*pbpeering.Peering var peerings []*pbpeering.Peering
// lookup the peering for each matching peered service // Lookup and return the peering corresponding to each name.
for _, peeredService := range peeredServices { for _, name := range peerNames {
readQuery := Query{ readQuery := Query{
Value: peeredService.PeerName, Value: name,
EnterpriseMeta: peeredService.Name.EnterpriseMeta, EnterpriseMeta: *structs.NodeEnterpriseMetaInPartition(entMeta.PartitionOrDefault()),
} }
_, peering, err := peeringReadTxn(tx, ws, readQuery) _, peering, err := peeringReadTxn(tx, ws, readQuery)
if err != nil { if err != nil {
@ -478,7 +480,6 @@ func (s *Store) PeeringsForService(ws memdb.WatchSet, serviceName string, entMet
} }
peerings = append(peerings, peering) peerings = append(peerings, peering)
} }
// see note above about idx
return idx, peerings, nil return idx, peerings, nil
} }
@ -597,50 +598,80 @@ func (r *Restore) PeeringTrustBundle(ptb *pbpeering.PeeringTrustBundle) error {
return nil return nil
} }
// readPeeredServicesFromConfigEntriesTxn queries exported-service config entries to return peers for serviceName // peersForServiceTxn returns the names of all peers that a service is exported to.
// in the form of a []structs.PeeredService. func peersForServiceTxn(
func readPeeredServicesFromConfigEntriesTxn(
tx ReadTxn, tx ReadTxn,
ws memdb.WatchSet, ws memdb.WatchSet,
serviceName string, serviceName string,
entMeta *acl.EnterpriseMeta, entMeta *acl.EnterpriseMeta,
) (uint64, []structs.PeeredService, error) { ) (uint64, []string, error) {
var results []structs.PeeredService // Exported service config entries are scoped to partitions so they are in the default namespace.
partitionMeta := structs.DefaultEnterpriseMetaInPartition(entMeta.PartitionOrDefault())
// Get all exported-service config entries for that have exports for serviceName. This assumes the result idx, rawEntry, err := configEntryTxn(tx, ws, structs.ExportedServices, partitionMeta.PartitionOrDefault(), partitionMeta)
// has exported services filtered to only those matching serviceName so no futher filtering is needed.
idx, exportedServicesEntries, err := getExportedServiceConfigEntriesTxn(tx, ws, serviceName, entMeta)
if err != nil { if err != nil {
return 0, nil, err return 0, nil, err
} }
if rawEntry == nil {
return idx, nil, err
}
// dedupe results by peer name entry, ok := rawEntry.(*structs.ExportedServicesConfigEntry)
resultSet := make(map[string]struct{}) if !ok {
// filter entries to only those that have a peer consumer defined return 0, nil, fmt.Errorf("unexpected type %T for pbpeering.Peering index", rawEntry)
for _, entry := range exportedServicesEntries { }
for _, service := range entry.Services {
// entries must have consumers
if service.Consumers == nil || len(service.Consumers) == 0 {
continue
}
for _, consumer := range service.Consumers {
// and consumers must have a peer
if consumer.PeerName == "" {
continue
}
// if we get here, we have a peer consumer, but we should dedupe peer names, so skip if it's already in the set
if _, ok := resultSet[consumer.PeerName]; ok {
continue
}
// if we got here, we can add to the result set var (
resultSet[consumer.PeerName] = struct{}{} wildcardNamespaceIdx = -1
result := structs.PeeredService{ wildcardServiceIdx = -1
Name: structs.NewServiceName(serviceName, entry.GetEnterpriseMeta()), exactMatchIdx = -1
PeerName: consumer.PeerName, )
}
results = append(results, result) // Ensure the metadata is defaulted since we make assertions against potentially empty values below.
} // In OSS this is a no-op.
if entMeta == nil {
entMeta = acl.DefaultEnterpriseMeta()
}
entMeta.Normalize()
// Services can be exported via wildcards or by their exact name:
// Namespace: *, Service: *
// Namespace: Exact, Service: *
// Namespace: Exact, Service: Exact
for i, service := range entry.Services {
switch {
case service.Namespace == structs.WildcardSpecifier:
wildcardNamespaceIdx = i
case service.Name == structs.WildcardSpecifier && service.Namespace == entMeta.NamespaceOrEmpty():
wildcardServiceIdx = i
case service.Name == serviceName && service.Namespace == entMeta.NamespaceOrEmpty():
exactMatchIdx = i
}
}
var results []string
// Prefer the exact match over the wildcard match. This matches how we handle intention precedence.
var targetIdx int
switch {
case exactMatchIdx >= 0:
targetIdx = exactMatchIdx
case wildcardServiceIdx >= 0:
targetIdx = wildcardServiceIdx
case wildcardNamespaceIdx >= 0:
targetIdx = wildcardNamespaceIdx
default:
return idx, results, nil
}
for _, c := range entry.Services[targetIdx].Consumers {
if c.PeerName != "" {
results = append(results, c.PeerName)
} }
} }
return idx, results, nil return idx, results, nil

View File

@ -907,7 +907,7 @@ func TestStateStore_PeeringsForService(t *testing.T) {
name string name string
services []structs.ServiceName services []structs.ServiceName
peerings []*pbpeering.Peering peerings []*pbpeering.Peering
entries []*structs.ExportedServicesConfigEntry entry *structs.ExportedServicesConfigEntry
query []string query []string
expect [][]*pbpeering.Peering expect [][]*pbpeering.Peering
expectIdx uint64 expectIdx uint64
@ -945,9 +945,10 @@ func TestStateStore_PeeringsForService(t *testing.T) {
} }
// Write the config entries. // Write the config entries.
for _, entry := range tc.entries { if tc.entry != nil {
lastIdx++ lastIdx++
require.NoError(t, s.EnsureConfigEntry(lastIdx, entry)) require.NoError(t, tc.entry.Normalize())
require.NoError(t, s.EnsureConfigEntry(lastIdx, tc.entry))
} }
// Query for peers. // Query for peers.
@ -976,7 +977,7 @@ func TestStateStore_PeeringsForService(t *testing.T) {
{Name: "foo"}, {Name: "foo"},
}, },
peerings: []*pbpeering.Peering{}, peerings: []*pbpeering.Peering{},
entries: []*structs.ExportedServicesConfigEntry{}, entry: nil,
query: []string{"foo"}, query: []string{"foo"},
expect: [][]*pbpeering.Peering{{}}, expect: [][]*pbpeering.Peering{{}},
}, },
@ -986,7 +987,7 @@ func TestStateStore_PeeringsForService(t *testing.T) {
{Name: "foo"}, {Name: "foo"},
}, },
peerings: []*pbpeering.Peering{}, peerings: []*pbpeering.Peering{},
entries: []*structs.ExportedServicesConfigEntry{}, entry: nil,
query: []string{"bar"}, query: []string{"bar"},
expect: [][]*pbpeering.Peering{{}}, expect: [][]*pbpeering.Peering{{}},
expectIdx: uint64(2), // catalog services max index expectIdx: uint64(2), // catalog services max index
@ -1001,24 +1002,22 @@ func TestStateStore_PeeringsForService(t *testing.T) {
{Name: "peer1", State: pbpeering.PeeringState_INITIAL}, {Name: "peer1", State: pbpeering.PeeringState_INITIAL},
{Name: "peer2", State: pbpeering.PeeringState_INITIAL}, {Name: "peer2", State: pbpeering.PeeringState_INITIAL},
}, },
entries: []*structs.ExportedServicesConfigEntry{ entry: &structs.ExportedServicesConfigEntry{
{ Name: "default",
Name: "ce1", Services: []structs.ExportedService{
Services: []structs.ExportedService{ {
{ Name: "foo",
Name: "foo", Consumers: []structs.ServiceConsumer{
Consumers: []structs.ServiceConsumer{ {
{ PeerName: "peer1",
PeerName: "peer1",
},
}, },
}, },
{ },
Name: "bar", {
Consumers: []structs.ServiceConsumer{ Name: "bar",
{ Consumers: []structs.ServiceConsumer{
PeerName: "peer2", {
}, PeerName: "peer2",
}, },
}, },
}, },
@ -1046,27 +1045,25 @@ func TestStateStore_PeeringsForService(t *testing.T) {
{Name: "peer2", State: pbpeering.PeeringState_INITIAL}, {Name: "peer2", State: pbpeering.PeeringState_INITIAL},
{Name: "peer3", State: pbpeering.PeeringState_INITIAL}, {Name: "peer3", State: pbpeering.PeeringState_INITIAL},
}, },
entries: []*structs.ExportedServicesConfigEntry{ entry: &structs.ExportedServicesConfigEntry{
{ Name: "default",
Name: "ce1", Services: []structs.ExportedService{
Services: []structs.ExportedService{ {
{ Name: "*",
Name: "*", Consumers: []structs.ServiceConsumer{
Consumers: []structs.ServiceConsumer{ {
{ PeerName: "peer1",
PeerName: "peer1", },
}, {
{ PeerName: "peer2",
PeerName: "peer2",
},
}, },
}, },
{ },
Name: "bar", {
Consumers: []structs.ServiceConsumer{ Name: "bar",
{ Consumers: []structs.ServiceConsumer{
PeerName: "peer3", {
}, PeerName: "peer3",
}, },
}, },
}, },
@ -1079,8 +1076,6 @@ func TestStateStore_PeeringsForService(t *testing.T) {
{Name: "peer2", State: pbpeering.PeeringState_INITIAL}, {Name: "peer2", State: pbpeering.PeeringState_INITIAL},
}, },
{ {
{Name: "peer1", State: pbpeering.PeeringState_INITIAL},
{Name: "peer2", State: pbpeering.PeeringState_INITIAL},
{Name: "peer3", State: pbpeering.PeeringState_INITIAL}, {Name: "peer3", State: pbpeering.PeeringState_INITIAL},
}, },
}, },

View File

@ -359,24 +359,22 @@ func TestPeeringService_TrustBundleRead(t *testing.T) {
} }
func TestPeeringService_TrustBundleListByService(t *testing.T) { func TestPeeringService_TrustBundleListByService(t *testing.T) {
// test executes the following scenario: // Test executes the following scenario:
// 0 - initial setup test server, state store, RPC client, verify empty results // 0 - Initial setup test server, state store, RPC client, verify empty results
// 1 - create a service, verify results still empty // 1 - Create a service, verify results still empty
// 2 - create a peering, verify results still empty // 2 - Create a peering, verify results still empty
// 3 - create a config entry, verify results still empty // 3 - Create a config entry, verify results still empty
// 4 - create trust bundles, verify bundles are returned // 4 - Create trust bundles, verify bundles are returned
// 5 - delete the config entry, verify results empty // 5 - Delete the config entry, verify results empty
// 6 - restore config entry, verify bundles are returned // 6 - Restore config entry, verify bundles are returned
// 7 - add peering, trust bundles, wildcard config entry, verify updated results are present // 7 - Add a second peering that the test service is not exported to
// 8 - delete first config entry, verify bundles are returned // 8 - Export the service to the new peering
// 9 - delete the service, verify results empty // 9 - Delete the service
// Note: these steps are dependent on each other by design so that we can verify that // Note: these steps are dependent on each other by design so that we can verify that
// combinations of services, peerings, trust bundles, and config entries all affect results // combinations of services, peerings, trust bundles, and config entries all affect results
// fixed for the test
nodeName := "test-node" nodeName := "test-node"
// keep track of index across steps
var lastIdx uint64 var lastIdx uint64
// Create test server // Create test server
@ -445,6 +443,7 @@ func TestPeeringService_TrustBundleListByService(t *testing.T) {
// Write any config entries // Write any config entries
for _, entry := range deps.entries { for _, entry := range deps.entries {
idx++ idx++
require.NoError(t, entry.Normalize())
require.NoError(t, store.EnsureConfigEntry(idx, entry)) require.NoError(t, store.EnsureConfigEntry(idx, entry))
} }
@ -460,6 +459,8 @@ func TestPeeringService_TrustBundleListByService(t *testing.T) {
// TODO(peering): see note on newTestServer, once we have a better server mock, // TODO(peering): see note on newTestServer, once we have a better server mock,
// we should add functionality here to verify errors from backend // we should add functionality here to verify errors from backend
verify := func(t *testing.T, tc *testCase) { verify := func(t *testing.T, tc *testCase) {
t.Helper()
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
t.Cleanup(cancel) t.Cleanup(cancel)
@ -478,7 +479,7 @@ func TestPeeringService_TrustBundleListByService(t *testing.T) {
// Execute scenario steps // Execute scenario steps
// ---------------------- // ----------------------
// 0 - initial empty state // 0 - Initial empty state.
// ----------------------- // -----------------------
verify(t, &testCase{ verify(t, &testCase{
req: &pbpeering.TrustBundleListByServiceRequest{ req: &pbpeering.TrustBundleListByServiceRequest{
@ -489,7 +490,7 @@ func TestPeeringService_TrustBundleListByService(t *testing.T) {
}, },
}) })
// 1 - create a service, verify results still empty // 1 - Create a service, verify results still empty.
// ------------------------------------------------ // ------------------------------------------------
lastIdx = setup(t, lastIdx, testDeps{services: []string{"foo"}}) lastIdx = setup(t, lastIdx, testDeps{services: []string{"foo"}})
verify(t, &testCase{ verify(t, &testCase{
@ -501,7 +502,7 @@ func TestPeeringService_TrustBundleListByService(t *testing.T) {
}, },
}) })
// 2 - create a peering, verify results still empty // 2 - Create a peering, verify results still empty.
// ------------------------------------------------ // ------------------------------------------------
lastIdx = setup(t, lastIdx, testDeps{ lastIdx = setup(t, lastIdx, testDeps{
peerings: []*pbpeering.Peering{ peerings: []*pbpeering.Peering{
@ -522,12 +523,12 @@ func TestPeeringService_TrustBundleListByService(t *testing.T) {
}, },
}) })
// 3 - create a config entry, verify results still empty // 3 - Create a config entry, verify results still empty.
// ----------------------------------------------------- // -----------------------------------------------------
lastIdx = setup(t, lastIdx, testDeps{ lastIdx = setup(t, lastIdx, testDeps{
entries: []*structs.ExportedServicesConfigEntry{ entries: []*structs.ExportedServicesConfigEntry{
{ {
Name: "export-foo", Name: "default",
Services: []structs.ExportedService{ Services: []structs.ExportedService{
{ {
Name: "foo", Name: "foo",
@ -550,7 +551,7 @@ func TestPeeringService_TrustBundleListByService(t *testing.T) {
}, },
}) })
// 4 - create trust bundles, verify bundles are returned // 4 - Create trust bundles, verify bundles are returned.
// ----------------------------------------------------- // -----------------------------------------------------
lastIdx = setup(t, lastIdx, testDeps{ lastIdx = setup(t, lastIdx, testDeps{
bundles: []*pbpeering.PeeringTrustBundle{ bundles: []*pbpeering.PeeringTrustBundle{
@ -576,10 +577,10 @@ func TestPeeringService_TrustBundleListByService(t *testing.T) {
}, },
}) })
// 5 - delete the config entry, verify results empty // 5 - Delete the config entry, verify results empty.
// ------------------------------------------------- // -------------------------------------------------
lastIdx++ lastIdx++
require.NoError(t, store.DeleteConfigEntry(lastIdx, structs.ExportedServices, "export-foo", nil)) require.NoError(t, store.DeleteConfigEntry(lastIdx, structs.ExportedServices, "default", nil))
verify(t, &testCase{ verify(t, &testCase{
req: &pbpeering.TrustBundleListByServiceRequest{ req: &pbpeering.TrustBundleListByServiceRequest{
ServiceName: "foo", ServiceName: "foo",
@ -589,12 +590,12 @@ func TestPeeringService_TrustBundleListByService(t *testing.T) {
}, },
}) })
// 6 - restore config entry, verify bundles are returned // 6 - Restore config entry, verify bundles are returned.
// ----------------------------------------------------- // -----------------------------------------------------
lastIdx = setup(t, lastIdx, testDeps{ lastIdx = setup(t, lastIdx, testDeps{
entries: []*structs.ExportedServicesConfigEntry{ entries: []*structs.ExportedServicesConfigEntry{
{ {
Name: "export-foo", Name: "default",
Services: []structs.ExportedService{ Services: []structs.ExportedService{
{ {
Name: "foo", Name: "foo",
@ -621,10 +622,9 @@ func TestPeeringService_TrustBundleListByService(t *testing.T) {
}, },
}) })
// 7 - add peering, trust bundles, wildcard config entry, verify updated results are present // 7 - Add new peer and trust bundle. It should be ignored because foo is not exported to it.
// ----------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------
lastIdx = setup(t, lastIdx, testDeps{ lastIdx = setup(t, lastIdx, testDeps{
services: []string{"bar"},
peerings: []*pbpeering.Peering{ peerings: []*pbpeering.Peering{
{ {
Name: "peer2", Name: "peer2",
@ -633,20 +633,6 @@ func TestPeeringService_TrustBundleListByService(t *testing.T) {
PeerServerAddresses: []string{"peer2-addr"}, PeerServerAddresses: []string{"peer2-addr"},
}, },
}, },
entries: []*structs.ExportedServicesConfigEntry{
{
Name: "export-all",
Services: []structs.ExportedService{
{
Name: structs.WildcardSpecifier,
Consumers: []structs.ServiceConsumer{
{PeerName: "peer1"},
{PeerName: "peer2"},
},
},
},
},
},
bundles: []*pbpeering.PeeringTrustBundle{ bundles: []*pbpeering.PeeringTrustBundle{
{ {
TrustDomain: "peer2.com", TrustDomain: "peer2.com",
@ -666,18 +652,28 @@ func TestPeeringService_TrustBundleListByService(t *testing.T) {
PeerName: "peer1", PeerName: "peer1",
RootPEMs: []string{"peer1-root-1"}, RootPEMs: []string{"peer1-root-1"},
}, },
{
TrustDomain: "peer2.com",
PeerName: "peer2",
RootPEMs: []string{"peer2-root-1"},
},
}, },
}, },
}) })
// 8 - delete first config entry, verify bundles are returned // 8 - Replace config entry to export all services to both peers
lastIdx++ // -----------------------------------------------------------------------------------------
require.NoError(t, store.DeleteConfigEntry(lastIdx, structs.ExportedServices, "export-foo", nil)) lastIdx = setup(t, lastIdx, testDeps{
entries: []*structs.ExportedServicesConfigEntry{
{
Name: "default",
Services: []structs.ExportedService{
{
Name: structs.WildcardSpecifier,
Consumers: []structs.ServiceConsumer{
{PeerName: "peer1"},
{PeerName: "peer2"},
},
},
},
},
},
})
verify(t, &testCase{ verify(t, &testCase{
req: &pbpeering.TrustBundleListByServiceRequest{ req: &pbpeering.TrustBundleListByServiceRequest{
ServiceName: "foo", ServiceName: "foo",

View File

@ -8,12 +8,6 @@ type PeeringToken struct {
PeerID string PeerID string
} }
// PeeredService is a service that has been configured with an exported-service config entry to be exported to a peer.
type PeeredService struct {
Name ServiceName
PeerName string
}
// NOTE: this is not serialized via msgpack so it can be changed without concern. // NOTE: this is not serialized via msgpack so it can be changed without concern.
type ExportedServiceList struct { type ExportedServiceList struct {
// Services is a list of exported services that apply to both standard // Services is a list of exported services that apply to both standard