From 8b58fa8afead0a0179f51ec6dd6c08bd9fca3ee9 Mon Sep 17 00:00:00 2001 From: freddygv Date: Mon, 23 May 2022 10:16:39 -0600 Subject: [PATCH] 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. --- agent/consul/state/config_entry.go | 67 +-------- agent/consul/state/config_entry_oss_test.go | 154 ++++++++++---------- agent/consul/state/peering.go | 123 ++++++++++------ agent/consul/state/peering_test.go | 79 +++++----- agent/rpc/peering/service_test.go | 90 ++++++------ agent/structs/peering.go | 6 - 6 files changed, 240 insertions(+), 279 deletions(-) diff --git a/agent/consul/state/config_entry.go b/agent/consul/state/config_entry.go index 195cc3b913..e2cd8600f1 100644 --- a/agent/consul/state/config_entry.go +++ b/agent/consul/state/config_entry.go @@ -603,6 +603,10 @@ func validateProposedConfigEntryInServiceGraph( wildcardEntMeta := kindName.WithWildcardNamespace() switch kindName.Kind { + case structs.ExportedServices, structs.MeshConfig: + // Exported services and mesh config do not influence discovery chains. + return nil + case structs.ProxyDefaults: // Check anything that has a discovery chain entry. In the future we could // somehow omit the ones that have a default protocol configured. @@ -1414,52 +1418,6 @@ func configEntryWithOverridesTxn( 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 // provided service, checking all relevant config entries. func protocolForService( @@ -1502,23 +1460,6 @@ func protocolForService( 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 { return configentry.NewKindName(c.GetKind(), c.GetName(), c.GetEnterpriseMeta()) } diff --git a/agent/consul/state/config_entry_oss_test.go b/agent/consul/state/config_entry_oss_test.go index 9f6a1ef44d..4d121ba32d 100644 --- a/agent/consul/state/config_entry_oss_test.go +++ b/agent/consul/state/config_entry_oss_test.go @@ -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 { name string - write []structs.ConfigEntry - query string - expect []*structs.ExportedServicesConfigEntry + write structs.ConfigEntry + expect []string } cases := []testCase{ { name: "empty everything", - write: []structs.ConfigEntry{}, - query: "foo", - expect: []*structs.ExportedServicesConfigEntry{}, + expect: nil, }, { - name: "no matching exported services", - write: []structs.ConfigEntry{ - &structs.ProxyConfigEntry{Name: "foo"}, - &structs.ProxyConfigEntry{Name: "bar"}, - &structs.ExportedServicesConfigEntry{ - Name: "baz", - Services: []structs.ExportedService{ - {Name: "baz"}, + name: "service is not exported", + write: &structs.ExportedServicesConfigEntry{ + Name: "default", + Services: []structs.ExportedService{ + { + Name: "not-" + queryName, + Consumers: []structs.ServiceConsumer{ + { + PeerName: "zip", + }, + }, }, }, }, - query: "foo", - expect: []*structs.ExportedServicesConfigEntry{}, + expect: nil, }, { - name: "exact match service name", - write: []structs.ConfigEntry{ - &structs.ExportedServicesConfigEntry{ - Name: "foo", - Services: []structs.ExportedService{ - {Name: "foo"}, + name: "wildcard name matches", + write: &structs.ExportedServicesConfigEntry{ + Name: "default", + Services: []structs.ExportedService{ + { + Name: "not-" + queryName, + Consumers: []structs.ServiceConsumer{ + { + PeerName: "zip", + }, + }, }, - }, - &structs.ExportedServicesConfigEntry{ - Name: "bar", - Services: []structs.ExportedService{ - {Name: "bar"}, - }, - }, - }, - query: "bar", - expect: []*structs.ExportedServicesConfigEntry{ - { - Name: "bar", - Services: []structs.ExportedService{ - {Name: "bar"}, + { + Name: structs.WildcardSpecifier, + Consumers: []structs.ServiceConsumer{ + { + PeerName: "bar", + }, + { + PeerName: "baz", + }, + }, }, }, }, + expect: []string{"bar", "baz"}, }, { - name: "wildcard match on service name", - write: []structs.ConfigEntry{ - &structs.ExportedServicesConfigEntry{ - Name: "foo", - Services: []structs.ExportedService{ - {Name: "foo"}, + name: "exact name takes precedence over wildcard", + write: &structs.ExportedServicesConfigEntry{ + Name: "default", + Services: []structs.ExportedService{ + { + Name: queryName, + Consumers: []structs.ServiceConsumer{ + { + PeerName: "baz", + }, + }, }, - }, - &structs.ExportedServicesConfigEntry{ - Name: "wildcard", - Services: []structs.ExportedService{ - {Name: structs.WildcardSpecifier}, - }, - }, - }, - query: "foo", - expect: []*structs.ExportedServicesConfigEntry{ - { - Name: "foo", - Services: []structs.ExportedService{ - {Name: "foo"}, - }, - }, - { - Name: "wildcard", - Services: []structs.ExportedService{ - {Name: structs.WildcardSpecifier}, + { + Name: structs.WildcardSpecifier, + Consumers: []structs.ServiceConsumer{ + { + PeerName: "zip", + }, + }, }, }, }, + expect: []string{"baz"}, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { s := testStateStore(t) + var lastIdx uint64 - // Write the entries. - for idx, entry := range tc.write { - require.NoError(t, s.EnsureConfigEntry(uint64(idx+1), entry)) + // Write the entry. + if tc.write != nil { + 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. tx := s.db.ReadTxn() 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.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. - require.Len(t, entries, len(tc.expect)) - for idx, got := range entries { - // ignore raft fields - got.ModifyIndex = 0 - got.CreateIndex = 0 - require.Equal(t, tc.expect[idx], got) - } + require.Len(t, peers, len(tc.expect)) + require.Equal(t, tc.expect, peers) }) } } diff --git a/agent/consul/state/peering.go b/agent/consul/state/peering.go index e566532b68..835e9642dc 100644 --- a/agent/consul/state/peering.go +++ b/agent/consul/state/peering.go @@ -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. // 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. +// // 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. func (s *Store) PeeringsForService(ws memdb.WatchSet, serviceName string, entMeta acl.EnterpriseMeta) (uint64, []*pbpeering.Peering, error) { tx := s.db.ReadTxn() 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. if svcIdx, svcExists, err := serviceExists(tx, ws, serviceName, &entMeta, ""); err != nil { return 0, nil, fmt.Errorf("failed to check if service exists: %w", err) + } 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 + } - // 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 that was last modified so caller can watch for changes - idx, peeredServices, err := readPeeredServicesFromConfigEntriesTxn(tx, ws, serviceName, meta) + + // Return the idx of the config entry so the caller can watch for changes. + idx, peerNames, err := peersForServiceTxn(tx, ws, serviceName, &entMeta) 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 - // lookup the peering for each matching peered service - for _, peeredService := range peeredServices { + // Lookup and return the peering corresponding to each name. + for _, name := range peerNames { readQuery := Query{ - Value: peeredService.PeerName, - EnterpriseMeta: peeredService.Name.EnterpriseMeta, + Value: name, + EnterpriseMeta: *structs.NodeEnterpriseMetaInPartition(entMeta.PartitionOrDefault()), } _, peering, err := peeringReadTxn(tx, ws, readQuery) if err != nil { @@ -478,7 +480,6 @@ func (s *Store) PeeringsForService(ws memdb.WatchSet, serviceName string, entMet } peerings = append(peerings, peering) } - // see note above about idx return idx, peerings, nil } @@ -597,50 +598,80 @@ func (r *Restore) PeeringTrustBundle(ptb *pbpeering.PeeringTrustBundle) error { return nil } -// readPeeredServicesFromConfigEntriesTxn queries exported-service config entries to return peers for serviceName -// in the form of a []structs.PeeredService. -func readPeeredServicesFromConfigEntriesTxn( +// peersForServiceTxn returns the names of all peers that a service is exported to. +func peersForServiceTxn( tx ReadTxn, ws memdb.WatchSet, serviceName string, entMeta *acl.EnterpriseMeta, -) (uint64, []structs.PeeredService, error) { - var results []structs.PeeredService +) (uint64, []string, error) { + // 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 - // has exported services filtered to only those matching serviceName so no futher filtering is needed. - idx, exportedServicesEntries, err := getExportedServiceConfigEntriesTxn(tx, ws, serviceName, entMeta) + idx, rawEntry, err := configEntryTxn(tx, ws, structs.ExportedServices, partitionMeta.PartitionOrDefault(), partitionMeta) if err != nil { return 0, nil, err } + if rawEntry == nil { + return idx, nil, err + } - // dedupe results by peer name - resultSet := make(map[string]struct{}) - // filter entries to only those that have a peer consumer defined - 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 - } + entry, ok := rawEntry.(*structs.ExportedServicesConfigEntry) + if !ok { + return 0, nil, fmt.Errorf("unexpected type %T for pbpeering.Peering index", rawEntry) + } - // if we got here, we can add to the result set - resultSet[consumer.PeerName] = struct{}{} - result := structs.PeeredService{ - Name: structs.NewServiceName(serviceName, entry.GetEnterpriseMeta()), - PeerName: consumer.PeerName, - } - results = append(results, result) - } + var ( + wildcardNamespaceIdx = -1 + wildcardServiceIdx = -1 + exactMatchIdx = -1 + ) + + // 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 diff --git a/agent/consul/state/peering_test.go b/agent/consul/state/peering_test.go index 53b80cb9c8..018cf81642 100644 --- a/agent/consul/state/peering_test.go +++ b/agent/consul/state/peering_test.go @@ -907,7 +907,7 @@ func TestStateStore_PeeringsForService(t *testing.T) { name string services []structs.ServiceName peerings []*pbpeering.Peering - entries []*structs.ExportedServicesConfigEntry + entry *structs.ExportedServicesConfigEntry query []string expect [][]*pbpeering.Peering expectIdx uint64 @@ -945,9 +945,10 @@ func TestStateStore_PeeringsForService(t *testing.T) { } // Write the config entries. - for _, entry := range tc.entries { + if tc.entry != nil { 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. @@ -976,7 +977,7 @@ func TestStateStore_PeeringsForService(t *testing.T) { {Name: "foo"}, }, peerings: []*pbpeering.Peering{}, - entries: []*structs.ExportedServicesConfigEntry{}, + entry: nil, query: []string{"foo"}, expect: [][]*pbpeering.Peering{{}}, }, @@ -986,7 +987,7 @@ func TestStateStore_PeeringsForService(t *testing.T) { {Name: "foo"}, }, peerings: []*pbpeering.Peering{}, - entries: []*structs.ExportedServicesConfigEntry{}, + entry: nil, query: []string{"bar"}, expect: [][]*pbpeering.Peering{{}}, expectIdx: uint64(2), // catalog services max index @@ -1001,24 +1002,22 @@ func TestStateStore_PeeringsForService(t *testing.T) { {Name: "peer1", State: pbpeering.PeeringState_INITIAL}, {Name: "peer2", State: pbpeering.PeeringState_INITIAL}, }, - entries: []*structs.ExportedServicesConfigEntry{ - { - Name: "ce1", - Services: []structs.ExportedService{ - { - Name: "foo", - Consumers: []structs.ServiceConsumer{ - { - PeerName: "peer1", - }, + entry: &structs.ExportedServicesConfigEntry{ + Name: "default", + Services: []structs.ExportedService{ + { + Name: "foo", + Consumers: []structs.ServiceConsumer{ + { + PeerName: "peer1", }, }, - { - Name: "bar", - Consumers: []structs.ServiceConsumer{ - { - PeerName: "peer2", - }, + }, + { + Name: "bar", + Consumers: []structs.ServiceConsumer{ + { + PeerName: "peer2", }, }, }, @@ -1046,27 +1045,25 @@ func TestStateStore_PeeringsForService(t *testing.T) { {Name: "peer2", State: pbpeering.PeeringState_INITIAL}, {Name: "peer3", State: pbpeering.PeeringState_INITIAL}, }, - entries: []*structs.ExportedServicesConfigEntry{ - { - Name: "ce1", - Services: []structs.ExportedService{ - { - Name: "*", - Consumers: []structs.ServiceConsumer{ - { - PeerName: "peer1", - }, - { - PeerName: "peer2", - }, + entry: &structs.ExportedServicesConfigEntry{ + Name: "default", + Services: []structs.ExportedService{ + { + Name: "*", + Consumers: []structs.ServiceConsumer{ + { + PeerName: "peer1", + }, + { + PeerName: "peer2", }, }, - { - Name: "bar", - Consumers: []structs.ServiceConsumer{ - { - PeerName: "peer3", - }, + }, + { + Name: "bar", + Consumers: []structs.ServiceConsumer{ + { + PeerName: "peer3", }, }, }, @@ -1079,8 +1076,6 @@ func TestStateStore_PeeringsForService(t *testing.T) { {Name: "peer2", State: pbpeering.PeeringState_INITIAL}, }, { - {Name: "peer1", State: pbpeering.PeeringState_INITIAL}, - {Name: "peer2", State: pbpeering.PeeringState_INITIAL}, {Name: "peer3", State: pbpeering.PeeringState_INITIAL}, }, }, diff --git a/agent/rpc/peering/service_test.go b/agent/rpc/peering/service_test.go index 0d30987da5..c1bb121ec0 100644 --- a/agent/rpc/peering/service_test.go +++ b/agent/rpc/peering/service_test.go @@ -359,24 +359,22 @@ func TestPeeringService_TrustBundleRead(t *testing.T) { } func TestPeeringService_TrustBundleListByService(t *testing.T) { - // test executes the following scenario: - // 0 - initial setup test server, state store, RPC client, verify empty results - // 1 - create a service, verify results still empty - // 2 - create a peering, verify results still empty - // 3 - create a config entry, verify results still empty - // 4 - create trust bundles, verify bundles are returned - // 5 - delete the config entry, verify results empty - // 6 - restore config entry, verify bundles are returned - // 7 - add peering, trust bundles, wildcard config entry, verify updated results are present - // 8 - delete first config entry, verify bundles are returned - // 9 - delete the service, verify results empty + // Test executes the following scenario: + // 0 - Initial setup test server, state store, RPC client, verify empty results + // 1 - Create a service, verify results still empty + // 2 - Create a peering, verify results still empty + // 3 - Create a config entry, verify results still empty + // 4 - Create trust bundles, verify bundles are returned + // 5 - Delete the config entry, verify results empty + // 6 - Restore config entry, verify bundles are returned + // 7 - Add a second peering that the test service is not exported to + // 8 - Export the service to the new peering + // 9 - Delete the service // 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 - // fixed for the test nodeName := "test-node" - // keep track of index across steps var lastIdx uint64 // Create test server @@ -445,6 +443,7 @@ func TestPeeringService_TrustBundleListByService(t *testing.T) { // Write any config entries for _, entry := range deps.entries { idx++ + require.NoError(t, entry.Normalize()) 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, // we should add functionality here to verify errors from backend verify := func(t *testing.T, tc *testCase) { + t.Helper() + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) t.Cleanup(cancel) @@ -478,7 +479,7 @@ func TestPeeringService_TrustBundleListByService(t *testing.T) { // Execute scenario steps // ---------------------- - // 0 - initial empty state + // 0 - Initial empty state. // ----------------------- verify(t, &testCase{ 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"}}) 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{ 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{ entries: []*structs.ExportedServicesConfigEntry{ { - Name: "export-foo", + Name: "default", Services: []structs.ExportedService{ { 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{ 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++ - require.NoError(t, store.DeleteConfigEntry(lastIdx, structs.ExportedServices, "export-foo", nil)) + require.NoError(t, store.DeleteConfigEntry(lastIdx, structs.ExportedServices, "default", nil)) verify(t, &testCase{ req: &pbpeering.TrustBundleListByServiceRequest{ 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{ entries: []*structs.ExportedServicesConfigEntry{ { - Name: "export-foo", + Name: "default", Services: []structs.ExportedService{ { 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{ - services: []string{"bar"}, peerings: []*pbpeering.Peering{ { Name: "peer2", @@ -633,20 +633,6 @@ func TestPeeringService_TrustBundleListByService(t *testing.T) { 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{ { TrustDomain: "peer2.com", @@ -666,18 +652,28 @@ func TestPeeringService_TrustBundleListByService(t *testing.T) { PeerName: "peer1", RootPEMs: []string{"peer1-root-1"}, }, - { - TrustDomain: "peer2.com", - PeerName: "peer2", - RootPEMs: []string{"peer2-root-1"}, - }, }, }, }) - // 8 - delete first config entry, verify bundles are returned - lastIdx++ - require.NoError(t, store.DeleteConfigEntry(lastIdx, structs.ExportedServices, "export-foo", nil)) + // 8 - Replace config entry to export all services to both peers + // ----------------------------------------------------------------------------------------- + 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{ req: &pbpeering.TrustBundleListByServiceRequest{ ServiceName: "foo", diff --git a/agent/structs/peering.go b/agent/structs/peering.go index bd0351bb36..1cf74bdaad 100644 --- a/agent/structs/peering.go +++ b/agent/structs/peering.go @@ -8,12 +8,6 @@ type PeeringToken struct { 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. type ExportedServiceList struct { // Services is a list of exported services that apply to both standard