Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update gateway-services table with endpoints #13217

Merged
merged 26 commits into from
May 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9c2cd37
update gateway-services table with endpoints
dhiaayachi May 25, 2022
98c4358
fix failing test
dhiaayachi May 25, 2022
1734104
remove unneeded config in test
dhiaayachi May 25, 2022
cdd5543
rename "endpoint" to "destination"
dhiaayachi May 25, 2022
aad5214
more endpoint renaming to destination in tests
dhiaayachi May 25, 2022
a312202
update isDestination based on service-defaults config entry creation
dhiaayachi May 25, 2022
2fd75dc
use a 3 state kind to be able to set the kind to unknown (when neithe…
dhiaayachi May 26, 2022
4e361d7
set unknown state to empty to avoid modifying alot of tests
dhiaayachi May 26, 2022
f6aafd8
fix logic to set the kind correctly on CRUD
dhiaayachi May 26, 2022
db9529f
fix failing tests
dhiaayachi May 26, 2022
3c5b85c
Merge pull request #13226 from hashicorp/egress-gtw/rename-destination
dhiaayachi May 26, 2022
2147d77
Merge branch 'main' into egress-gtw/tgtw-gateway-services
dhiaayachi May 26, 2022
9949f4d
add missing tests and fix service delete
dhiaayachi May 26, 2022
35a80c5
fix failing test
dhiaayachi May 27, 2022
1e6803f
Apply suggestions from code review
dhiaayachi May 27, 2022
711bdc5
fix a bug with kind and add relevant test
dhiaayachi May 27, 2022
fcd4a2e
fix compile error
dhiaayachi May 27, 2022
8a9ff79
fix failing tests
dhiaayachi May 27, 2022
5d66a08
add kind to clone
dhiaayachi May 27, 2022
36d52fa
fix failing tests
dhiaayachi May 30, 2022
9ed9345
fix failing tests in catalog endpoint
dhiaayachi May 30, 2022
042b874
fix service dump test
dhiaayachi May 30, 2022
ac6c9b4
Apply suggestions from code review
dhiaayachi May 31, 2022
2440051
remove duplicate tests
dhiaayachi May 31, 2022
f900906
rename consts and fix kind when no destination is defined in the serv…
dhiaayachi May 31, 2022
f964e09
rename Kind to ServiceKind and change switch to use .(type)
dhiaayachi May 31, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions agent/consul/catalog_endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3054,6 +3054,7 @@ func TestCatalog_GatewayServices_TerminatingGateway(t *testing.T) {
CertFile: "api/client.crt",
KeyFile: "api/client.key",
SNI: "my-domain",
ServiceKind: structs.GatewayServiceKindService,
},
{
Service: structs.NewServiceName("db", nil),
Expand All @@ -3062,6 +3063,7 @@ func TestCatalog_GatewayServices_TerminatingGateway(t *testing.T) {
CAFile: "",
CertFile: "",
KeyFile: "",
ServiceKind: structs.GatewayServiceKindService,
},
{
Service: structs.NewServiceName("redis", nil),
Expand Down Expand Up @@ -3206,6 +3208,7 @@ func TestCatalog_GatewayServices_BothGateways(t *testing.T) {
Service: structs.NewServiceName("api", nil),
Gateway: structs.NewServiceName("gateway", nil),
GatewayKind: structs.ServiceKindTerminatingGateway,
ServiceKind: structs.GatewayServiceKindService,
},
}

Expand Down Expand Up @@ -3428,11 +3431,13 @@ service "gateway" {
Service: structs.NewServiceName("db", nil),
Gateway: structs.NewServiceName("gateway", nil),
GatewayKind: structs.ServiceKindTerminatingGateway,
ServiceKind: structs.GatewayServiceKindService,
},
{
Service: structs.NewServiceName("db_replica", nil),
Gateway: structs.NewServiceName("gateway", nil),
GatewayKind: structs.ServiceKindTerminatingGateway,
ServiceKind: structs.GatewayServiceKindUnknown,
},
}

Expand Down
2 changes: 2 additions & 0 deletions agent/consul/internal_endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1122,6 +1122,7 @@ func TestInternal_GatewayServiceDump_Terminating(t *testing.T) {
Gateway: structs.NewServiceName("terminating-gateway", nil),
Service: structs.NewServiceName("db", nil),
GatewayKind: "terminating-gateway",
ServiceKind: structs.GatewayServiceKindService,
},
},
{
Expand Down Expand Up @@ -1155,6 +1156,7 @@ func TestInternal_GatewayServiceDump_Terminating(t *testing.T) {
Gateway: structs.NewServiceName("terminating-gateway", nil),
Service: structs.NewServiceName("db", nil),
GatewayKind: "terminating-gateway",
ServiceKind: structs.GatewayServiceKindService,
},
},
{
Expand Down
124 changes: 113 additions & 11 deletions agent/consul/state/catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -846,9 +846,16 @@ func ensureServiceTxn(tx WriteTxn, idx uint64, node string, preserveIndexes bool
}

if svc.PeerName == "" {
// Check if this service is covered by a gateway's wildcard specifier
if err = checkGatewayWildcardsAndUpdate(tx, idx, svc); err != nil {
return fmt.Errorf("failed updating gateway mapping: %s", err)
// Do not associate non-typical services with gateways or consul services
if svc.Kind == structs.ServiceKindTypical && svc.Service != "consul" {
// Check if this service is covered by a gateway's wildcard specifier, we force the service kind to a gateway-service here as that take precedence
sn := structs.NewServiceName(svc.Service, &svc.EnterpriseMeta)
if err = checkGatewayWildcardsAndUpdate(tx, idx, &sn, structs.GatewayServiceKindService); err != nil {
return fmt.Errorf("failed updating gateway mapping: %s", err)
}
if err = checkGatewayAndUpdate(tx, idx, &sn, structs.GatewayServiceKindService); err != nil {
return fmt.Errorf("failed updating gateway mapping: %s", err)
}
}
}
if err := upsertKindServiceName(tx, idx, svc.Kind, svc.CompoundServiceName()); err != nil {
Expand Down Expand Up @@ -3483,6 +3490,10 @@ func terminatingConfigGatewayServices(

var gatewayServices structs.GatewayServices
for _, svc := range entry.Services {
kind, err := GatewayServiceKind(tx, svc.Name, &svc.EnterpriseMeta)
if err != nil {
return false, nil, fmt.Errorf("failed to get gateway service kind for service %s: %v", svc.Name, err)
}
mapping := &structs.GatewayService{
Gateway: gateway,
Service: structs.NewServiceName(svc.Name, &svc.EnterpriseMeta),
Expand All @@ -3491,13 +3502,42 @@ func terminatingConfigGatewayServices(
CertFile: svc.CertFile,
CAFile: svc.CAFile,
SNI: svc.SNI,
ServiceKind: kind,
}

gatewayServices = append(gatewayServices, mapping)
}
return false, gatewayServices, nil
}

func GatewayServiceKind(tx ReadTxn, name string, entMeta *acl.EnterpriseMeta) (structs.GatewayServiceKind, error) {
serviceIter, err := tx.First(tableServices, indexService, Query{
Value: name,
EnterpriseMeta: *entMeta,
})
if err != nil {
return structs.GatewayServiceKindUnknown, err
}
if serviceIter != nil {
return structs.GatewayServiceKindService, err
}

_, entry, err := configEntryTxn(tx, nil, structs.ServiceDefaults, name, entMeta)
if err != nil {
return structs.GatewayServiceKindUnknown, err
}
if entry != nil {
sd, ok := entry.(*structs.ServiceConfigEntry)
if !ok {
return structs.GatewayServiceKindUnknown, fmt.Errorf("invalid config entry type %T", entry)
}
if sd.Destination != nil {
return structs.GatewayServiceKindDestination, nil
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
}
}
return structs.GatewayServiceKindService, nil

Would this be appropriate? Or are you elsewhere going to interpret "unknown" as "service"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be dealt with in the watch receiving part #13196
Once this PR got merged we will update #13196 to have the right logic.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason I added the unknown kind is to differentiate between existing services/destinations and references in the tgtw to non existing, it will help for debugging purpose.

}
return structs.GatewayServiceKindUnknown, nil
}

// updateGatewayNamespace is used to target all services within a namespace
func updateGatewayNamespace(tx WriteTxn, idx uint64, service *structs.GatewayService, entMeta *acl.EnterpriseMeta) error {
if entMeta == nil {
Expand Down Expand Up @@ -3538,6 +3578,41 @@ func updateGatewayNamespace(tx WriteTxn, idx uint64, service *structs.GatewaySer
return err
}
}
entries, err := tx.Get(tableConfigEntries, indexID+"_prefix", ConfigEntryKindQuery{Kind: structs.ServiceDefaults, EnterpriseMeta: *entMeta})
if err != nil {
return fmt.Errorf("failed querying entries: %s", err)
}
for entry := entries.Next(); entry != nil; entry = entries.Next() {
e := entry.(*structs.ServiceConfigEntry)
if e.Destination == nil {
continue
}

sn := structs.ServiceName{
Name: e.Name,
EnterpriseMeta: e.EnterpriseMeta,
}
existing, err := tx.First(tableGatewayServices, indexID, service.Gateway, sn, service.Port)
if err != nil {
return fmt.Errorf("gateway service lookup failed: %s", err)
}
if existing != nil {
// If there's an existing service associated with this gateway then we skip it.
// This means the service was specified on its own, and the service entry overrides the wildcard entry.
continue
}

mapping := service.Clone()

mapping.Service = structs.NewServiceName(e.Name, &service.Service.EnterpriseMeta)
mapping.ServiceKind = structs.GatewayServiceKindDestination
mapping.FromWildcard = true

err = updateGatewayService(tx, idx, mapping)
if err != nil {
return err
}
}

// Also store a mapping for the wildcard so that the TLS creds can be pulled
// for new services registered in its namespace
Expand Down Expand Up @@ -3585,25 +3660,21 @@ func updateGatewayService(tx WriteTxn, idx uint64, mapping *structs.GatewayServi
// checkWildcardForGatewaysAndUpdate checks whether a service matches a
// wildcard definition in gateway config entries and if so adds it the the
// gateway-services table.
func checkGatewayWildcardsAndUpdate(tx WriteTxn, idx uint64, svc *structs.NodeService) error {
// Do not associate non-typical services with gateways or consul services
if svc.Kind != structs.ServiceKindTypical || svc.Service == "consul" {
return nil
}

func checkGatewayWildcardsAndUpdate(tx WriteTxn, idx uint64, svc *structs.ServiceName, kind structs.GatewayServiceKind) error {
sn := structs.ServiceName{Name: structs.WildcardSpecifier, EnterpriseMeta: svc.EnterpriseMeta}
svcGateways, err := tx.Get(tableGatewayServices, indexService, sn)
if err != nil {
return fmt.Errorf("failed gateway lookup for %q: %s", svc.Service, err)
return fmt.Errorf("failed gateway lookup for %q: %s", svc.Name, err)
}
for service := svcGateways.Next(); service != nil; service = svcGateways.Next() {
if wildcardSvc, ok := service.(*structs.GatewayService); ok && wildcardSvc != nil {

// Copy the wildcard mapping and modify it
gatewaySvc := wildcardSvc.Clone()

gatewaySvc.Service = structs.NewServiceName(svc.Service, &svc.EnterpriseMeta)
gatewaySvc.Service = structs.NewServiceName(svc.Name, &svc.EnterpriseMeta)
gatewaySvc.FromWildcard = true
gatewaySvc.ServiceKind = kind

if err = updateGatewayService(tx, idx, gatewaySvc); err != nil {
return fmt.Errorf("Failed to associate service %q with gateway %q", gatewaySvc.Service.String(), gatewaySvc.Gateway.String())
Expand All @@ -3613,6 +3684,31 @@ func checkGatewayWildcardsAndUpdate(tx WriteTxn, idx uint64, svc *structs.NodeSe
return nil
}

// checkGatewayAndUpdate checks whether a service matches a
// wildcard definition in gateway config entries and if so adds it the the
// gateway-services table.
func checkGatewayAndUpdate(tx WriteTxn, idx uint64, svc *structs.ServiceName, kind structs.GatewayServiceKind) error {
sn := structs.ServiceName{Name: svc.Name, EnterpriseMeta: svc.EnterpriseMeta}
svcGateways, err := tx.First(tableGatewayServices, indexService, sn)
if err != nil {
return fmt.Errorf("failed gateway lookup for %q: %s", svc.Name, err)
}

if service, ok := svcGateways.(*structs.GatewayService); ok && service != nil {
// Copy the wildcard mapping and modify it
gatewaySvc := service.Clone()

gatewaySvc.Service = structs.NewServiceName(svc.Name, &svc.EnterpriseMeta)
gatewaySvc.ServiceKind = kind

if err = updateGatewayService(tx, idx, gatewaySvc); err != nil {
return fmt.Errorf("Failed to associate service %q with gateway %q", gatewaySvc.Service.String(), gatewaySvc.Gateway.String())
}
}

return nil
}

func cleanupGatewayWildcards(tx WriteTxn, idx uint64, svc *structs.ServiceNode) error {
// Clean up association between service name and gateways if needed
sn := structs.ServiceName{Name: svc.ServiceName, EnterpriseMeta: svc.EnterpriseMeta}
Expand Down Expand Up @@ -3643,6 +3739,12 @@ func cleanupGatewayWildcards(tx WriteTxn, idx uint64, svc *structs.ServiceNode)
if err := deleteGatewayServiceTopologyMapping(tx, idx, m); err != nil {
return fmt.Errorf("failed to reconcile mesh topology for gateway: %v", err)
}
} else {
kind, err := GatewayServiceKind(tx, m.Service.Name, &m.Service.EnterpriseMeta)
if err != nil {
return fmt.Errorf("failed to get gateway service kind for service %s: %v", svc.ServiceName, err)
}
checkGatewayAndUpdate(tx, idx, &structs.ServiceName{Name: m.Service.Name, EnterpriseMeta: m.Service.EnterpriseMeta}, kind)
}
}
return nil
Expand Down
110 changes: 110 additions & 0 deletions agent/consul/state/catalog_events_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1473,6 +1473,18 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
},
WantEvents: []stream.Event{
testServiceHealthEvent(t, "srv1", evNodeUnchanged),
testServiceHealthDeregistrationEvent(t,
"tgate1",
evConnectTopic,
evServiceTermingGateway("srv1"),
evTerminatingGatewayVirtualIPs("srv1"),
),
testServiceHealthEvent(t,
"tgate1",
evConnectTopic,
evNodeUnchanged,
evServiceUnchanged,
evServiceTermingGateway("srv1")),
},
})
run(t, eventsTestCase{
Expand Down Expand Up @@ -1505,6 +1517,18 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
},
WantEvents: []stream.Event{
testServiceHealthDeregistrationEvent(t, "srv1"),
testServiceHealthDeregistrationEvent(t,
"tgate1",
evConnectTopic,
evServiceTermingGateway("srv1"),
evTerminatingGatewayVirtualIPs("srv1"),
),
testServiceHealthEvent(t,
"tgate1",
evConnectTopic,
evNodeUnchanged,
evServiceUnchanged,
evServiceTermingGateway("srv1")),
},
})
run(t, eventsTestCase{
Expand Down Expand Up @@ -1625,6 +1649,92 @@ func TestServiceHealthEventsFromChanges(t *testing.T) {
evTerminatingGatewayVirtualIPs("srv1", "srv2")),
},
})
run(t, eventsTestCase{
Name: "terminating gateway destination service-defaults",
Setup: func(s *Store, tx *txn) error {
configEntry := &structs.TerminatingGatewayConfigEntry{
Kind: structs.TerminatingGateway,
Name: "tgate1",
Services: []structs.LinkedService{
{
Name: "destination1",
EnterpriseMeta: *structs.DefaultEnterpriseMetaInDefaultPartition(),
},
},
EnterpriseMeta: *structs.DefaultEnterpriseMetaInDefaultPartition(),
}
err := ensureConfigEntryTxn(tx, tx.Index, configEntry)
if err != nil {
return err
}
return s.ensureRegistrationTxn(tx, tx.Index, false,
testServiceRegistration(t, "tgate1", regTerminatingGateway), false)
},
Mutate: func(s *Store, tx *txn) error {
configEntryDest := &structs.ServiceConfigEntry{
Kind: structs.ServiceDefaults,
Name: "destination1",
Destination: &structs.DestinationConfig{Port: 9000, Address: "kafka.test.com"},
}
return ensureConfigEntryTxn(tx, tx.Index, configEntryDest)
},
WantEvents: []stream.Event{
testServiceHealthDeregistrationEvent(t,
"tgate1",
evConnectTopic,
evServiceTermingGateway("destination1"),
evTerminatingGatewayVirtualIPs("destination1")),
testServiceHealthEvent(t,
"tgate1",
evConnectTopic,
evNodeUnchanged,
evServiceUnchanged,
evServiceTermingGateway("destination1"),
evTerminatingGatewayVirtualIPs("destination1"),
),
},
})

run(t, eventsTestCase{
Name: "terminating gateway destination service-defaults wildcard",
Setup: func(s *Store, tx *txn) error {
configEntry := &structs.TerminatingGatewayConfigEntry{
Kind: structs.TerminatingGateway,
Name: "tgate1",
Services: []structs.LinkedService{
{
Name: "*",
EnterpriseMeta: *structs.DefaultEnterpriseMetaInDefaultPartition(),
},
},
EnterpriseMeta: *structs.DefaultEnterpriseMetaInDefaultPartition(),
}
err := ensureConfigEntryTxn(tx, tx.Index, configEntry)
if err != nil {
return err
}
return s.ensureRegistrationTxn(tx, tx.Index, false,
testServiceRegistration(t, "tgate1", regTerminatingGateway), false)
},
Mutate: func(s *Store, tx *txn) error {
configEntryDest := &structs.ServiceConfigEntry{
Kind: structs.ServiceDefaults,
Name: "destination1",
Destination: &structs.DestinationConfig{Port: 9000, Address: "kafka.test.com"},
}
return ensureConfigEntryTxn(tx, tx.Index, configEntryDest)
},
WantEvents: []stream.Event{
testServiceHealthEvent(t,
"tgate1",
evConnectTopic,
evNodeUnchanged,
evServiceUnchanged,
evServiceTermingGateway("destination1"),
evTerminatingGatewayVirtualIPs("*"),
),
},
})
}

func (tc eventsTestCase) run(t *testing.T) {
Expand Down
Loading