diff --git a/agent/consul/catalog_endpoint_test.go b/agent/consul/catalog_endpoint_test.go index 78ac4c36bde3..67929575a702 100644 --- a/agent/consul/catalog_endpoint_test.go +++ b/agent/consul/catalog_endpoint_test.go @@ -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), @@ -3062,6 +3063,7 @@ func TestCatalog_GatewayServices_TerminatingGateway(t *testing.T) { CAFile: "", CertFile: "", KeyFile: "", + ServiceKind: structs.GatewayServiceKindService, }, { Service: structs.NewServiceName("redis", nil), @@ -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, }, } @@ -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, }, } diff --git a/agent/consul/internal_endpoint_test.go b/agent/consul/internal_endpoint_test.go index 07cc8587cebe..9354b4f66413 100644 --- a/agent/consul/internal_endpoint_test.go +++ b/agent/consul/internal_endpoint_test.go @@ -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, }, }, { @@ -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, }, }, { diff --git a/agent/consul/state/catalog.go b/agent/consul/state/catalog.go index 97d9e81ebf41..9e6340382516 100644 --- a/agent/consul/state/catalog.go +++ b/agent/consul/state/catalog.go @@ -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 { @@ -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), @@ -3491,6 +3502,7 @@ func terminatingConfigGatewayServices( CertFile: svc.CertFile, CAFile: svc.CAFile, SNI: svc.SNI, + ServiceKind: kind, } gatewayServices = append(gatewayServices, mapping) @@ -3498,6 +3510,34 @@ func terminatingConfigGatewayServices( 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 + } + } + 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 { @@ -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 @@ -3585,16 +3660,11 @@ 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 { @@ -3602,8 +3672,9 @@ func checkGatewayWildcardsAndUpdate(tx WriteTxn, idx uint64, svc *structs.NodeSe // 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()) @@ -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} @@ -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 diff --git a/agent/consul/state/catalog_events_test.go b/agent/consul/state/catalog_events_test.go index 0b455543e3b6..129b834b82e6 100644 --- a/agent/consul/state/catalog_events_test.go +++ b/agent/consul/state/catalog_events_test.go @@ -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{ @@ -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{ @@ -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) { diff --git a/agent/consul/state/catalog_test.go b/agent/consul/state/catalog_test.go index e9f8aac771ff..3c650ecd867d 100644 --- a/agent/consul/state/catalog_test.go +++ b/agent/consul/state/catalog_test.go @@ -5010,6 +5010,7 @@ func TestStateStore_GatewayServices_Terminating(t *testing.T) { CreateIndex: 21, ModifyIndex: 21, }, + ServiceKind: structs.GatewayServiceKindService, }, { Service: structs.NewServiceName("db", nil), @@ -5019,6 +5020,7 @@ func TestStateStore_GatewayServices_Terminating(t *testing.T) { CreateIndex: 21, ModifyIndex: 21, }, + ServiceKind: structs.GatewayServiceKindService, }, } assert.Equal(t, expect, out) @@ -5052,6 +5054,7 @@ func TestStateStore_GatewayServices_Terminating(t *testing.T) { CreateIndex: 21, ModifyIndex: 21, }, + ServiceKind: structs.GatewayServiceKindService, }, { Service: structs.NewServiceName("db", nil), @@ -5061,6 +5064,7 @@ func TestStateStore_GatewayServices_Terminating(t *testing.T) { CreateIndex: 21, ModifyIndex: 21, }, + ServiceKind: structs.GatewayServiceKindService, }, } assert.Equal(t, expect, out) @@ -5111,6 +5115,7 @@ func TestStateStore_GatewayServices_Terminating(t *testing.T) { CreateIndex: 22, ModifyIndex: 22, }, + ServiceKind: structs.GatewayServiceKindService, }, { Service: structs.NewServiceName("db", nil), @@ -5120,6 +5125,7 @@ func TestStateStore_GatewayServices_Terminating(t *testing.T) { CreateIndex: 22, ModifyIndex: 22, }, + ServiceKind: structs.GatewayServiceKindService, }, } assert.Equal(t, expect, out) @@ -5147,6 +5153,7 @@ func TestStateStore_GatewayServices_Terminating(t *testing.T) { CreateIndex: 22, ModifyIndex: 22, }, + ServiceKind: structs.GatewayServiceKindService, }, { Service: structs.NewServiceName("db", nil), @@ -5156,6 +5163,7 @@ func TestStateStore_GatewayServices_Terminating(t *testing.T) { CreateIndex: 22, ModifyIndex: 22, }, + ServiceKind: structs.GatewayServiceKindService, }, { Service: structs.NewServiceName("redis", nil), @@ -5170,6 +5178,7 @@ func TestStateStore_GatewayServices_Terminating(t *testing.T) { CreateIndex: 23, ModifyIndex: 23, }, + ServiceKind: structs.GatewayServiceKindService, }, } assert.Equal(t, expect, out) @@ -5197,6 +5206,7 @@ func TestStateStore_GatewayServices_Terminating(t *testing.T) { CreateIndex: 22, ModifyIndex: 22, }, + ServiceKind: structs.GatewayServiceKindService, }, { Service: structs.NewServiceName("db", nil), @@ -5206,6 +5216,7 @@ func TestStateStore_GatewayServices_Terminating(t *testing.T) { CreateIndex: 22, ModifyIndex: 22, }, + ServiceKind: structs.GatewayServiceKindService, }, } assert.Equal(t, expect, out) @@ -5237,6 +5248,7 @@ func TestStateStore_GatewayServices_Terminating(t *testing.T) { CreateIndex: 25, ModifyIndex: 25, }, + ServiceKind: structs.GatewayServiceKindService, }, } assert.Equal(t, expect, out) @@ -5364,6 +5376,7 @@ func TestStateStore_GatewayServices_ServiceDeletion(t *testing.T) { CreateIndex: 19, ModifyIndex: 19, }, + ServiceKind: structs.GatewayServiceKindService, }, } assert.Equal(t, expect, out) @@ -5402,8 +5415,8 @@ func TestStateStore_GatewayServices_ServiceDeletion(t *testing.T) { // Delete a service specified directly. assert.Nil(t, s.DeleteService(20, "foo", "db", nil, "")) - // Only the watch for other-gateway should fire, since its association to db came from a wildcard - assert.False(t, watchFired(ws)) + // The watch will fire because we need to update the gateway-services kind + assert.True(t, watchFired(ws)) assert.True(t, watchFired(otherWS)) // db should remain in the original gateway @@ -5420,7 +5433,7 @@ func TestStateStore_GatewayServices_ServiceDeletion(t *testing.T) { CAFile: "my_ca.pem", RaftIndex: structs.RaftIndex{ CreateIndex: 19, - ModifyIndex: 19, + ModifyIndex: 20, }, }, } @@ -6301,6 +6314,7 @@ func TestStateStore_DumpGatewayServices(t *testing.T) { CreateIndex: 21, ModifyIndex: 21, }, + ServiceKind: structs.GatewayServiceKindService, }, { Service: structs.NewServiceName("db", nil), @@ -6310,6 +6324,7 @@ func TestStateStore_DumpGatewayServices(t *testing.T) { CreateIndex: 21, ModifyIndex: 21, }, + ServiceKind: structs.GatewayServiceKindService, }, } assert.Equal(t, expect, out) @@ -6360,6 +6375,7 @@ func TestStateStore_DumpGatewayServices(t *testing.T) { CreateIndex: 21, ModifyIndex: 21, }, + ServiceKind: structs.GatewayServiceKindService, }, { Service: structs.NewServiceName("db", nil), @@ -6369,6 +6385,7 @@ func TestStateStore_DumpGatewayServices(t *testing.T) { CreateIndex: 21, ModifyIndex: 21, }, + ServiceKind: structs.GatewayServiceKindService, }, } assert.Equal(t, expect, out) @@ -6398,6 +6415,7 @@ func TestStateStore_DumpGatewayServices(t *testing.T) { CreateIndex: 21, ModifyIndex: 21, }, + ServiceKind: structs.GatewayServiceKindService, }, { Service: structs.NewServiceName("db", nil), @@ -6407,6 +6425,7 @@ func TestStateStore_DumpGatewayServices(t *testing.T) { CreateIndex: 21, ModifyIndex: 21, }, + ServiceKind: structs.GatewayServiceKindService, }, { Service: structs.NewServiceName("redis", nil), @@ -6421,6 +6440,7 @@ func TestStateStore_DumpGatewayServices(t *testing.T) { CreateIndex: 22, ModifyIndex: 22, }, + ServiceKind: structs.GatewayServiceKindService, }, } assert.Equal(t, expect, out) @@ -6450,6 +6470,7 @@ func TestStateStore_DumpGatewayServices(t *testing.T) { CreateIndex: 21, ModifyIndex: 21, }, + ServiceKind: structs.GatewayServiceKindService, }, { Service: structs.NewServiceName("db", nil), @@ -6459,6 +6480,7 @@ func TestStateStore_DumpGatewayServices(t *testing.T) { CreateIndex: 21, ModifyIndex: 21, }, + ServiceKind: structs.GatewayServiceKindService, }, } assert.Equal(t, expect, out) @@ -6492,6 +6514,7 @@ func TestStateStore_DumpGatewayServices(t *testing.T) { CreateIndex: 24, ModifyIndex: 24, }, + ServiceKind: structs.GatewayServiceKindService, }, } assert.Equal(t, expect, out) @@ -6549,6 +6572,7 @@ func TestStateStore_DumpGatewayServices(t *testing.T) { CreateIndex: 24, ModifyIndex: 24, }, + ServiceKind: structs.GatewayServiceKindService, }, { Service: structs.NewServiceName("api", nil), diff --git a/agent/consul/state/config_entry.go b/agent/consul/state/config_entry.go index 4743e325522d..195cc3b913c0 100644 --- a/agent/consul/state/config_entry.go +++ b/agent/consul/state/config_entry.go @@ -344,6 +344,27 @@ func deleteConfigEntryTxn(tx WriteTxn, idx uint64, kind, name string, entMeta *a return fmt.Errorf("failed updating gateway-services index: %v", err) } } + + c := existing.(structs.ConfigEntry) + switch x := c.(type) { + case *structs.ServiceConfigEntry: + if x.Destination != nil { + gsKind, err := GatewayServiceKind(tx, sn.Name, &sn.EnterpriseMeta) + if err != nil { + return fmt.Errorf("failed to get gateway service kind for service %s: %v", sn.Name, err) + } + if gsKind == structs.GatewayServiceKindDestination { + gsKind = structs.GatewayServiceKindUnknown + } + if err := checkGatewayWildcardsAndUpdate(tx, idx, &structs.ServiceName{Name: c.GetName(), EnterpriseMeta: *c.GetEnterpriseMeta()}, gsKind); err != nil { + return fmt.Errorf("failed updating gateway mapping: %s", err) + } + if err := checkGatewayAndUpdate(tx, idx, &structs.ServiceName{Name: c.GetName(), EnterpriseMeta: *c.GetEnterpriseMeta()}, gsKind); err != nil { + return fmt.Errorf("failed updating gateway mapping: %s", err) + } + } + } + // Also clean up associations in the mesh topology table for ingress gateways if kind == structs.IngressGateway { if _, err := tx.DeleteAll(tableMeshTopology, indexDownstream, sn); err != nil { @@ -376,6 +397,7 @@ func insertConfigEntryWithTxn(tx WriteTxn, idx uint64, conf structs.ConfigEntry) } // If the config entry is for a terminating or ingress gateway we update the memdb table // that associates gateways <-> services. + if conf.GetKind() == structs.TerminatingGateway || conf.GetKind() == structs.IngressGateway { err := updateGatewayServices(tx, idx, conf, conf.GetEnterpriseMeta()) if err != nil { @@ -383,6 +405,26 @@ func insertConfigEntryWithTxn(tx WriteTxn, idx uint64, conf structs.ConfigEntry) } } + switch conf.GetKind() { + case structs.ServiceDefaults: + if conf.(*structs.ServiceConfigEntry).Destination != nil { + sn := structs.ServiceName{Name: conf.GetName(), EnterpriseMeta: *conf.GetEnterpriseMeta()} + gsKind, err := GatewayServiceKind(tx, sn.Name, &sn.EnterpriseMeta) + if gsKind == structs.GatewayServiceKindUnknown { + gsKind = structs.GatewayServiceKindDestination + } + if err != nil { + return fmt.Errorf("failed updating gateway mapping: %s", err) + } + if err := checkGatewayWildcardsAndUpdate(tx, idx, &sn, gsKind); err != nil { + return fmt.Errorf("failed updating gateway mapping: %s", err) + } + if err := checkGatewayAndUpdate(tx, idx, &sn, gsKind); err != nil { + return fmt.Errorf("failed updating gateway mapping: %s", err) + } + } + } + // Insert the config entry and update the index if err := tx.Insert(tableConfigEntries, conf); err != nil { return fmt.Errorf("failed inserting config entry: %s", err) diff --git a/agent/consul/state/config_entry_test.go b/agent/consul/state/config_entry_test.go index 8cb4d8f51dc5..5d2cdf9fd280 100644 --- a/agent/consul/state/config_entry_test.go +++ b/agent/consul/state/config_entry_test.go @@ -73,8 +73,8 @@ func TestStore_ConfigEntry(t *testing.T) { serviceConf.Protocol = "http" require.NoError(t, s.EnsureConfigEntry(5, serviceConf)) require.True(t, watchFired(ws)) -} +} func TestStore_ConfigEntryCAS(t *testing.T) { s := testConfigStateStore(t) @@ -310,6 +310,497 @@ func TestStore_ConfigEntries(t *testing.T) { Protocol: "tcp", })) require.True(t, watchFired(ws)) + +} + +func TestStore_ServiceDefaults_Kind_Destination(t *testing.T) { + s := testConfigStateStore(t) + + Gtwy := &structs.TerminatingGatewayConfigEntry{ + Kind: structs.TerminatingGateway, + Name: "Gtwy1", + Services: []structs.LinkedService{ + { + Name: "dest1", + }, + }, + } + + // Create + require.NoError(t, s.EnsureConfigEntry(0, Gtwy)) + + destination := &structs.ServiceConfigEntry{ + Kind: structs.ServiceDefaults, + Name: "dest1", + Destination: &structs.DestinationConfig{}, + } + + _, gatewayServices, err := s.GatewayServices(nil, "Gtwy1", nil) + require.NoError(t, err) + require.Len(t, gatewayServices, 1) + require.Equal(t, gatewayServices[0].ServiceKind, structs.GatewayServiceKindUnknown) + + ws := memdb.NewWatchSet() + _, _, err = s.GatewayServices(ws, "Gtwy1", nil) + require.NoError(t, err) + + // Create + require.NoError(t, s.EnsureConfigEntry(0, destination)) + + //Watch is fired because we transitioned to a destination, by default we assume it's not. + require.True(t, watchFired(ws)) + + _, gatewayServices, err = s.GatewayServices(ws, "Gtwy1", nil) + require.NoError(t, err) + require.Len(t, gatewayServices, 1) + require.Equal(t, gatewayServices[0].ServiceKind, structs.GatewayServiceKindDestination) + + ws = memdb.NewWatchSet() + _, _, err = s.GatewayServices(ws, "Gtwy1", nil) + require.NoError(t, err) + + require.NoError(t, s.DeleteConfigEntry(6, structs.ServiceDefaults, destination.Name, &destination.EnterpriseMeta)) + + //Watch is fired because we transitioned to a destination, by default we assume it's not. + require.True(t, watchFired(ws)) + + _, gatewayServices, err = s.GatewayServices(ws, "Gtwy1", nil) + require.NoError(t, err) + require.Len(t, gatewayServices, 1) + require.Equal(t, gatewayServices[0].ServiceKind, structs.GatewayServiceKindUnknown) + +} + +func TestStore_ServiceDefaults_Kind_NotDestination(t *testing.T) { + s := testConfigStateStore(t) + + Gtwy := &structs.TerminatingGatewayConfigEntry{ + Kind: structs.TerminatingGateway, + Name: "Gtwy1", + Services: []structs.LinkedService{ + { + Name: "dest1", + }, + }, + } + + // Create + require.NoError(t, s.EnsureConfigEntry(0, Gtwy)) + + destination := &structs.ServiceConfigEntry{ + Kind: structs.ServiceDefaults, + Name: "dest1", + } + + _, gatewayServices, err := s.GatewayServices(nil, "Gtwy1", nil) + require.NoError(t, err) + require.Len(t, gatewayServices, 1) + require.Equal(t, gatewayServices[0].ServiceKind, structs.GatewayServiceKindUnknown) + + ws := memdb.NewWatchSet() + _, _, err = s.GatewayServices(ws, "Gtwy1", nil) + require.NoError(t, err) + + // Create + require.NoError(t, s.EnsureConfigEntry(0, destination)) + + //Watch is fired because we transitioned to a destination, by default we assume it's not. + require.False(t, watchFired(ws)) + + _, gatewayServices, err = s.GatewayServices(ws, "Gtwy1", nil) + require.NoError(t, err) + require.Len(t, gatewayServices, 1) + require.Equal(t, gatewayServices[0].ServiceKind, structs.GatewayServiceKindUnknown) + + ws = memdb.NewWatchSet() + _, _, err = s.GatewayServices(ws, "Gtwy1", nil) + require.NoError(t, err) + + require.NoError(t, s.DeleteConfigEntry(6, structs.ServiceDefaults, destination.Name, &destination.EnterpriseMeta)) + + //Watch is fired because we transitioned to a destination, by default we assume it's not. + require.False(t, watchFired(ws)) + + _, gatewayServices, err = s.GatewayServices(ws, "Gtwy1", nil) + require.NoError(t, err) + require.Len(t, gatewayServices, 1) + require.Equal(t, gatewayServices[0].ServiceKind, structs.GatewayServiceKindUnknown) + +} + +func TestStore_Service_TerminatingGateway_Kind_Service_Destination(t *testing.T) { + s := testConfigStateStore(t) + + Gtwy := &structs.TerminatingGatewayConfigEntry{ + Kind: structs.TerminatingGateway, + Name: "Gtwy1", + Services: []structs.LinkedService{ + { + Name: "web", + }, + }, + } + + // Create + require.NoError(t, s.EnsureConfigEntry(0, Gtwy)) + + service := &structs.NodeService{ + Kind: structs.ServiceKindTypical, + Service: "web", + } + destination := &structs.ServiceConfigEntry{ + Kind: structs.ServiceDefaults, + Name: "web", + Destination: &structs.DestinationConfig{}, + } + + _, gatewayServices, err := s.GatewayServices(nil, "Gtwy1", nil) + require.NoError(t, err) + require.Len(t, gatewayServices, 1) + require.Equal(t, gatewayServices[0].ServiceKind, structs.GatewayServiceKindUnknown) + + ws := memdb.NewWatchSet() + _, _, err = s.GatewayServices(ws, "Gtwy1", nil) + require.NoError(t, err) + + // Create + require.NoError(t, s.EnsureNode(0, &structs.Node{Node: "node1"})) + require.NoError(t, s.EnsureService(0, "node1", service)) + + //Watch is fired because we transitioned to a destination, by default we assume it's not. + require.True(t, watchFired(ws)) + + _, gatewayServices, err = s.GatewayServices(ws, "Gtwy1", nil) + require.NoError(t, err) + require.Len(t, gatewayServices, 1) + require.Equal(t, gatewayServices[0].ServiceKind, structs.GatewayServiceKindService) + + require.NoError(t, s.EnsureConfigEntry(0, destination)) + + _, gatewayServices, err = s.GatewayServices(nil, "Gtwy1", nil) + require.NoError(t, err) + require.Len(t, gatewayServices, 1) + require.Equal(t, gatewayServices[0].ServiceKind, structs.GatewayServiceKindService) + + ws = memdb.NewWatchSet() + _, _, err = s.GatewayServices(ws, "Gtwy1", nil) + require.NoError(t, err) + + require.NoError(t, s.DeleteService(6, "node1", service.ID, &service.EnterpriseMeta, "")) + + //Watch is fired because we transitioned to a destination, by default we assume it's not. + require.True(t, watchFired(ws)) + + _, gatewayServices, err = s.GatewayServices(ws, "Gtwy1", nil) + require.NoError(t, err) + require.Len(t, gatewayServices, 1) + require.Equal(t, gatewayServices[0].ServiceKind, structs.GatewayServiceKindDestination) + +} + +func TestStore_Service_TerminatingGateway_Kind_Destination_Service(t *testing.T) { + s := testConfigStateStore(t) + + Gtwy := &structs.TerminatingGatewayConfigEntry{ + Kind: structs.TerminatingGateway, + Name: "Gtwy1", + Services: []structs.LinkedService{ + { + Name: "web", + }, + }, + } + + // Create + require.NoError(t, s.EnsureConfigEntry(0, Gtwy)) + + service := &structs.NodeService{ + Kind: structs.ServiceKindTypical, + Service: "web", + } + destination := &structs.ServiceConfigEntry{ + Kind: structs.ServiceDefaults, + Name: "web", + Destination: &structs.DestinationConfig{}, + } + + _, gatewayServices, err := s.GatewayServices(nil, "Gtwy1", nil) + require.NoError(t, err) + require.Len(t, gatewayServices, 1) + require.Equal(t, gatewayServices[0].ServiceKind, structs.GatewayServiceKindUnknown) + + ws := memdb.NewWatchSet() + _, _, err = s.GatewayServices(ws, "Gtwy1", nil) + require.NoError(t, err) + + // Create + require.NoError(t, s.EnsureConfigEntry(0, destination)) + + _, gatewayServices, err = s.GatewayServices(nil, "Gtwy1", nil) + require.NoError(t, err) + require.Len(t, gatewayServices, 1) + require.Equal(t, gatewayServices[0].ServiceKind, structs.GatewayServiceKindDestination) + + require.NoError(t, s.EnsureNode(0, &structs.Node{Node: "node1"})) + require.NoError(t, s.EnsureService(0, "node1", service)) + + //Watch is fired because we transitioned to a destination, by default we assume it's not. + require.True(t, watchFired(ws)) + + _, gatewayServices, err = s.GatewayServices(ws, "Gtwy1", nil) + require.NoError(t, err) + require.Len(t, gatewayServices, 1) + require.Equal(t, gatewayServices[0].ServiceKind, structs.GatewayServiceKindService) + + ws = memdb.NewWatchSet() + _, _, err = s.GatewayServices(ws, "Gtwy1", nil) + require.NoError(t, err) + + require.NoError(t, s.DeleteService(6, "node1", service.ID, &service.EnterpriseMeta, "")) + + //Watch is fired because we transitioned to a destination, by default we assume it's not. + require.True(t, watchFired(ws)) + + _, gatewayServices, err = s.GatewayServices(ws, "Gtwy1", nil) + require.NoError(t, err) + require.Len(t, gatewayServices, 1) + require.Equal(t, gatewayServices[0].ServiceKind, structs.GatewayServiceKindDestination) + +} + +func TestStore_Service_TerminatingGateway_Kind_Service(t *testing.T) { + s := testConfigStateStore(t) + + Gtwy := &structs.TerminatingGatewayConfigEntry{ + Kind: structs.TerminatingGateway, + Name: "Gtwy1", + Services: []structs.LinkedService{ + { + Name: "web", + }, + }, + } + + // Create + require.NoError(t, s.EnsureConfigEntry(0, Gtwy)) + + service := &structs.NodeService{ + Kind: structs.ServiceKindTypical, + Service: "web", + } + + _, gatewayServices, err := s.GatewayServices(nil, "Gtwy1", nil) + require.NoError(t, err) + require.Len(t, gatewayServices, 1) + require.Equal(t, gatewayServices[0].ServiceKind, structs.GatewayServiceKindUnknown) + + ws := memdb.NewWatchSet() + _, _, err = s.GatewayServices(ws, "Gtwy1", nil) + require.NoError(t, err) + + // Create + require.NoError(t, s.EnsureNode(0, &structs.Node{Node: "node1"})) + require.NoError(t, s.EnsureService(0, "node1", service)) + + //Watch is fired because we transitioned to a destination, by default we assume it's not. + require.True(t, watchFired(ws)) + + _, gatewayServices, err = s.GatewayServices(ws, "Gtwy1", nil) + require.NoError(t, err) + require.Len(t, gatewayServices, 1) + require.Equal(t, gatewayServices[0].ServiceKind, structs.GatewayServiceKindService) + + ws = memdb.NewWatchSet() + _, _, err = s.GatewayServices(ws, "Gtwy1", nil) + require.NoError(t, err) + + require.NoError(t, s.DeleteService(6, "node1", service.ID, &service.EnterpriseMeta, "")) + + //Watch is fired because we transitioned to a destination, by default we assume it's not. + require.True(t, watchFired(ws)) + + _, gatewayServices, err = s.GatewayServices(ws, "Gtwy1", nil) + require.NoError(t, err) + require.Len(t, gatewayServices, 1) + require.Equal(t, gatewayServices[0].ServiceKind, structs.GatewayServiceKindUnknown) + +} + +func TestStore_ServiceDefaults_Kind_Destination_Wildcard(t *testing.T) { + s := testConfigStateStore(t) + + Gtwy := &structs.TerminatingGatewayConfigEntry{ + Kind: structs.TerminatingGateway, + Name: "Gtwy1", + Services: []structs.LinkedService{ + { + Name: "*", + }, + }, + } + + // Create + require.NoError(t, s.EnsureConfigEntry(0, Gtwy)) + + destination := &structs.ServiceConfigEntry{ + Kind: structs.ServiceDefaults, + Name: "dest1", + Destination: &structs.DestinationConfig{}, + } + + _, gatewayServices, err := s.GatewayServices(nil, "Gtwy1", nil) + require.NoError(t, err) + require.Len(t, gatewayServices, 0) + + ws := memdb.NewWatchSet() + _, _, err = s.GatewayServices(ws, "Gtwy1", nil) + // Create + require.NoError(t, s.EnsureConfigEntry(0, destination)) + require.NoError(t, err) + + require.True(t, watchFired(ws)) + + _, gatewayServices, err = s.GatewayServices(ws, "Gtwy1", nil) + require.NoError(t, err) + require.Len(t, gatewayServices, 1) + require.Equal(t, gatewayServices[0].ServiceKind, structs.GatewayServiceKindDestination) + + ws = memdb.NewWatchSet() + _, _, err = s.GatewayServices(ws, "Gtwy1", nil) + require.NoError(t, err) + + require.NoError(t, s.DeleteConfigEntry(6, structs.ServiceDefaults, destination.Name, &destination.EnterpriseMeta)) + + //Watch is fired because we transitioned to a destination, by default we assume it's not. + require.True(t, watchFired(ws)) + + _, gatewayServices, err = s.GatewayServices(ws, "Gtwy1", nil) + require.NoError(t, err) + require.Len(t, gatewayServices, 1) + require.Equal(t, gatewayServices[0].ServiceKind, structs.GatewayServiceKindUnknown) +} + +func TestStore_Service_TerminatingGateway_Kind_Service_Wildcard(t *testing.T) { + s := testConfigStateStore(t) + + Gtwy := &structs.TerminatingGatewayConfigEntry{ + Kind: structs.TerminatingGateway, + Name: "Gtwy1", + Services: []structs.LinkedService{ + { + Name: "*", + }, + }, + } + + // Create + require.NoError(t, s.EnsureConfigEntry(0, Gtwy)) + + service := &structs.NodeService{ + Kind: structs.ServiceKindTypical, + Service: "web", + } + + _, gatewayServices, err := s.GatewayServices(nil, "Gtwy1", nil) + require.NoError(t, err) + require.Len(t, gatewayServices, 0) + + ws := memdb.NewWatchSet() + _, _, err = s.GatewayServices(ws, "Gtwy1", nil) + require.NoError(t, err) + + // Create + require.NoError(t, s.EnsureNode(0, &structs.Node{Node: "node1"})) + require.NoError(t, s.EnsureService(0, "node1", service)) + + //Watch is fired because we transitioned to a destination, by default we assume it's not. + require.True(t, watchFired(ws)) + + _, gatewayServices, err = s.GatewayServices(ws, "Gtwy1", nil) + require.NoError(t, err) + require.Len(t, gatewayServices, 1) + require.Equal(t, gatewayServices[0].ServiceKind, structs.GatewayServiceKindService) + + ws = memdb.NewWatchSet() + _, _, err = s.GatewayServices(ws, "Gtwy1", nil) + require.NoError(t, err) + + require.NoError(t, s.DeleteService(6, "node1", service.ID, &service.EnterpriseMeta, "")) + + //Watch is fired because we transitioned to a destination, by default we assume it's not. + require.True(t, watchFired(ws)) + + _, gatewayServices, err = s.GatewayServices(ws, "Gtwy1", nil) + require.NoError(t, err) + require.Len(t, gatewayServices, 0) +} + +func TestStore_Service_TerminatingGateway_Kind_Service_Destination_Wildcard(t *testing.T) { + s := testConfigStateStore(t) + + Gtwy := &structs.TerminatingGatewayConfigEntry{ + Kind: structs.TerminatingGateway, + Name: "Gtwy1", + Services: []structs.LinkedService{ + { + Name: "*", + }, + }, + } + + // Create + require.NoError(t, s.EnsureConfigEntry(0, Gtwy)) + + service := &structs.NodeService{ + Kind: structs.ServiceKindTypical, + Service: "web", + } + destination := &structs.ServiceConfigEntry{ + Kind: structs.ServiceDefaults, + Name: "web", + Destination: &structs.DestinationConfig{}, + } + + _, gatewayServices, err := s.GatewayServices(nil, "Gtwy1", nil) + require.NoError(t, err) + require.Len(t, gatewayServices, 0) + + ws := memdb.NewWatchSet() + _, _, err = s.GatewayServices(ws, "Gtwy1", nil) + require.NoError(t, err) + + // Create + require.NoError(t, s.EnsureConfigEntry(0, destination)) + + _, gatewayServices, err = s.GatewayServices(nil, "Gtwy1", nil) + require.NoError(t, err) + require.Len(t, gatewayServices, 1) + require.Equal(t, gatewayServices[0].ServiceKind, structs.GatewayServiceKindDestination) + + require.NoError(t, s.EnsureNode(0, &structs.Node{Node: "node1"})) + require.NoError(t, s.EnsureService(0, "node1", service)) + + //Watch is fired because we transitioned to a destination, by default we assume it's not. + require.True(t, watchFired(ws)) + + _, gatewayServices, err = s.GatewayServices(ws, "Gtwy1", nil) + require.NoError(t, err) + require.Len(t, gatewayServices, 1) + require.Equal(t, gatewayServices[0].ServiceKind, structs.GatewayServiceKindService) + + ws = memdb.NewWatchSet() + _, _, err = s.GatewayServices(ws, "Gtwy1", nil) + require.NoError(t, err) + + require.NoError(t, s.DeleteService(6, "node1", service.ID, &service.EnterpriseMeta, "")) + + //Watch is fired because we transitioned to a destination, by default we assume it's not. + require.True(t, watchFired(ws)) + + _, gatewayServices, err = s.GatewayServices(ws, "Gtwy1", nil) + require.NoError(t, err) + require.Len(t, gatewayServices, 0) + } func TestStore_ConfigEntry_GraphValidation(t *testing.T) { diff --git a/agent/structs/config_entry.go b/agent/structs/config_entry.go index d8953205e584..5382da3af81a 100644 --- a/agent/structs/config_entry.go +++ b/agent/structs/config_entry.go @@ -105,7 +105,7 @@ type ServiceConfigEntry struct { Expose ExposeConfig `json:",omitempty"` ExternalSNI string `json:",omitempty" alias:"external_sni"` UpstreamConfig *UpstreamConfiguration `json:",omitempty" alias:"upstream_config"` - Endpoint *EndpointConfig `json:",omitempty"` + Destination *DestinationConfig `json:",omitempty"` MaxInboundConnections int `json:",omitempty" alias:"max_inbound_connections"` Meta map[string]string `json:",omitempty"` @@ -180,8 +180,8 @@ func (e *ServiceConfigEntry) Validate() error { validationErr := validateConfigEntryMeta(e.Meta) // External endpoints are invalid with an existing service's upstream configuration - if e.UpstreamConfig != nil && e.Endpoint != nil { - validationErr = multierror.Append(validationErr, errors.New("UpstreamConfig and Endpoint are mutually exclusive for service defaults")) + if e.UpstreamConfig != nil && e.Destination != nil { + validationErr = multierror.Append(validationErr, errors.New("UpstreamConfig and Destination are mutually exclusive for service defaults")) return validationErr } @@ -200,13 +200,13 @@ func (e *ServiceConfigEntry) Validate() error { } } - if e.Endpoint != nil { - if err := validateEndpointAddress(e.Endpoint.Address); err != nil { - validationErr = multierror.Append(validationErr, fmt.Errorf("Endpoint address is invalid %w", err)) + if e.Destination != nil { + if err := validateEndpointAddress(e.Destination.Address); err != nil { + validationErr = multierror.Append(validationErr, fmt.Errorf("Destination address is invalid %w", err)) } - if e.Endpoint.Port < 1 || e.Endpoint.Port > 65535 { - validationErr = multierror.Append(validationErr, fmt.Errorf("Invalid Port number %d", e.Endpoint.Port)) + if e.Destination.Port < 1 || e.Destination.Port > 65535 { + validationErr = multierror.Append(validationErr, fmt.Errorf("Invalid Port number %d", e.Destination.Port)) } } @@ -292,8 +292,8 @@ func (c *UpstreamConfiguration) Clone() *UpstreamConfiguration { return &c2 } -// EndpointConfig represents a virtual service, i.e. one that is external to Consul -type EndpointConfig struct { +// DestinationConfig represents a virtual service, i.e. one that is external to Consul +type DestinationConfig struct { // Address of the endpoint; hostname, IP, or CIDR Address string `json:",omitempty"` diff --git a/agent/structs/config_entry_gateways.go b/agent/structs/config_entry_gateways.go index 0f8917098998..c0abcd59db12 100644 --- a/agent/structs/config_entry_gateways.go +++ b/agent/structs/config_entry_gateways.go @@ -586,19 +586,28 @@ func (e *TerminatingGatewayConfigEntry) Warnings() []string { return warnings } +type GatewayServiceKind string + +const ( + GatewayServiceKindUnknown GatewayServiceKind = "" + GatewayServiceKindDestination GatewayServiceKind = "destination" + GatewayServiceKindService GatewayServiceKind = "service" +) + // GatewayService is used to associate gateways with their linked services. type GatewayService struct { Gateway ServiceName Service ServiceName GatewayKind ServiceKind - Port int `json:",omitempty"` - Protocol string `json:",omitempty"` - Hosts []string `json:",omitempty"` - CAFile string `json:",omitempty"` - CertFile string `json:",omitempty"` - KeyFile string `json:",omitempty"` - SNI string `json:",omitempty"` - FromWildcard bool `json:",omitempty"` + Port int `json:",omitempty"` + Protocol string `json:",omitempty"` + Hosts []string `json:",omitempty"` + CAFile string `json:",omitempty"` + CertFile string `json:",omitempty"` + KeyFile string `json:",omitempty"` + SNI string `json:",omitempty"` + FromWildcard bool `json:",omitempty"` + ServiceKind GatewayServiceKind `json:",omitempty"` RaftIndex } @@ -635,6 +644,7 @@ func (g *GatewayService) IsSame(o *GatewayService) bool { g.CertFile == o.CertFile && g.KeyFile == o.KeyFile && g.SNI == o.SNI && + g.ServiceKind == o.ServiceKind && g.FromWildcard == o.FromWildcard } @@ -653,5 +663,6 @@ func (g *GatewayService) Clone() *GatewayService { SNI: g.SNI, FromWildcard: g.FromWildcard, RaftIndex: g.RaftIndex, + ServiceKind: g.ServiceKind, } } diff --git a/agent/structs/config_entry_test.go b/agent/structs/config_entry_test.go index 70db92aaa9ef..afbd737f8909 100644 --- a/agent/structs/config_entry_test.go +++ b/agent/structs/config_entry_test.go @@ -428,12 +428,12 @@ func TestDecodeConfigEntry(t *testing.T) { }, }, { - name: "service-defaults-with-endpoint", + name: "service-defaults-with-destination", snake: ` kind = "service-defaults" name = "external" protocol = "tcp" - endpoint { + destination { address = "1.2.3.4/24" port = 8080 } @@ -442,7 +442,7 @@ func TestDecodeConfigEntry(t *testing.T) { Kind = "service-defaults" Name = "external" Protocol = "tcp" - Endpoint { + Destination { Address = "1.2.3.4/24" Port = 8080 } @@ -451,7 +451,7 @@ func TestDecodeConfigEntry(t *testing.T) { Kind: "service-defaults", Name: "external", Protocol: "tcp", - Endpoint: &EndpointConfig{ + Destination: &DestinationConfig{ Address: "1.2.3.4/24", Port: 8080, }, @@ -2421,79 +2421,79 @@ func TestServiceConfigEntry(t *testing.T) { EnterpriseMeta: *DefaultEnterpriseMetaInDefaultPartition(), }, }, - "validate: missing endpoint address": { + "validate: missing destination address": { entry: &ServiceConfigEntry{ Kind: ServiceDefaults, Name: "external", Protocol: "tcp", - Endpoint: &EndpointConfig{ + Destination: &DestinationConfig{ Address: "", Port: 443, }, }, validateErr: "Could not validate address", }, - "validate: endpoint ipv4 address": { + "validate: destination ipv4 address": { entry: &ServiceConfigEntry{ Kind: ServiceDefaults, Name: "external", Protocol: "tcp", - Endpoint: &EndpointConfig{ + Destination: &DestinationConfig{ Address: "1.2.3.4", Port: 443, }, }, }, - "validate: endpoint ipv4 CIDR address": { + "validate: destination ipv4 CIDR address": { entry: &ServiceConfigEntry{ Kind: ServiceDefaults, Name: "external", Protocol: "tcp", - Endpoint: &EndpointConfig{ + Destination: &DestinationConfig{ Address: "10.0.0.1/16", Port: 8080, }, }, }, - "validate: endpoint ipv6 address": { + "validate: destination ipv6 address": { entry: &ServiceConfigEntry{ Kind: ServiceDefaults, Name: "external", Protocol: "tcp", - Endpoint: &EndpointConfig{ + Destination: &DestinationConfig{ Address: "2001:0db8:0000:8a2e:0370:7334:1234:5678", Port: 443, }, }, }, - "valid endpoint shortened ipv6 address": { + "valid destination shortened ipv6 address": { entry: &ServiceConfigEntry{ Kind: ServiceDefaults, Name: "external", Protocol: "tcp", - Endpoint: &EndpointConfig{ + Destination: &DestinationConfig{ Address: "2001:db8::8a2e:370:7334", Port: 443, }, }, }, - "validate: endpoint ipv6 CIDR address": { + "validate: destination ipv6 CIDR address": { entry: &ServiceConfigEntry{ Kind: ServiceDefaults, Name: "external", Protocol: "tcp", - Endpoint: &EndpointConfig{ + Destination: &DestinationConfig{ Address: "2001:db8::8a2e:370:7334/64", Port: 443, }, }, }, - "validate: invalid endpoint port": { + "validate: invalid destination port": { entry: &ServiceConfigEntry{ Kind: ServiceDefaults, Name: "external", Protocol: "tcp", - Endpoint: &EndpointConfig{ + Destination: &DestinationConfig{ Address: "2001:db8::8a2e:370:7334/64", }, }, @@ -2504,7 +2504,7 @@ func TestServiceConfigEntry(t *testing.T) { Kind: ServiceDefaults, Name: "external", Protocol: "tcp", - Endpoint: &EndpointConfig{ + Destination: &DestinationConfig{ Address: "*external.com", Port: 443, }, @@ -2516,7 +2516,7 @@ func TestServiceConfigEntry(t *testing.T) { Kind: ServiceDefaults, Name: "external", Protocol: "tcp", - Endpoint: &EndpointConfig{ + Destination: &DestinationConfig{ Address: "..hello.", Port: 443, }, @@ -2528,7 +2528,7 @@ func TestServiceConfigEntry(t *testing.T) { Kind: ServiceDefaults, Name: "external", Protocol: "http", - Endpoint: &EndpointConfig{ + Destination: &DestinationConfig{ Address: "*", Port: 443, }, diff --git a/api/config_entry.go b/api/config_entry.go index ac88c1d560f3..79c50b62ee6b 100644 --- a/api/config_entry.go +++ b/api/config_entry.go @@ -179,8 +179,8 @@ type UpstreamConfig struct { MeshGateway MeshGatewayConfig `json:",omitempty" alias:"mesh_gateway" ` } -// EndpointConfig represents a virtual service, i.e. one that is external to Consul -type EndpointConfig struct { +// DestinationConfig represents a virtual service, i.e. one that is external to Consul +type DestinationConfig struct { // Address of the endpoint; hostname, IP, or CIDR Address string `json:",omitempty"` @@ -229,7 +229,7 @@ type ServiceConfigEntry struct { Expose ExposeConfig `json:",omitempty"` ExternalSNI string `json:",omitempty" alias:"external_sni"` UpstreamConfig *UpstreamConfiguration `json:",omitempty" alias:"upstream_config"` - Endpoint *EndpointConfig `json:",omitempty"` + Destination *DestinationConfig `json:",omitempty"` Meta map[string]string `json:",omitempty"` CreateIndex uint64 ModifyIndex uint64 diff --git a/api/config_entry_test.go b/api/config_entry_test.go index 5b93783db246..a7b02c3a8981 100644 --- a/api/config_entry_test.go +++ b/api/config_entry_test.go @@ -106,16 +106,16 @@ func TestAPI_ConfigEntries(t *testing.T) { }, } - endpoint := &EndpointConfig{ + dest := &DestinationConfig{ Address: "my.example.com", Port: 80, } service2 := &ServiceConfigEntry{ - Kind: ServiceDefaults, - Name: "bar", - Protocol: "tcp", - Endpoint: endpoint, + Kind: ServiceDefaults, + Name: "bar", + Protocol: "tcp", + Destination: dest, } // set it @@ -191,7 +191,7 @@ func TestAPI_ConfigEntries(t *testing.T) { require.Equal(t, service2.Kind, readService.Kind) require.Equal(t, service2.Name, readService.Name) require.Equal(t, service2.Protocol, readService.Protocol) - require.Equal(t, endpoint, readService.Endpoint) + require.Equal(t, dest, readService.Destination) } } @@ -530,7 +530,7 @@ func TestDecodeConfigEntry(t *testing.T) { "Kind": "service-defaults", "Name": "external", "Protocol": "http", - "Endpoint": { + "Destination": { "Address": "1.2.3.4/24", "Port": 443 } @@ -540,7 +540,7 @@ func TestDecodeConfigEntry(t *testing.T) { Kind: "service-defaults", Name: "external", Protocol: "http", - Endpoint: &EndpointConfig{ + Destination: &DestinationConfig{ Address: "1.2.3.4/24", Port: 443, }, diff --git a/command/config/write/config_write_test.go b/command/config/write/config_write_test.go index 7ff60620d8ee..566e2ab9b1a2 100644 --- a/command/config/write/config_write_test.go +++ b/command/config/write/config_write_test.go @@ -793,7 +793,7 @@ func TestParseConfigEntry(t *testing.T) { }, }, { - name: "service-defaults: kitchen sink (endpoint edition)", + name: "service-defaults: kitchen sink (destination edition)", snake: ` kind = "service-defaults" name = "main" @@ -810,7 +810,7 @@ func TestParseConfigEntry(t *testing.T) { outbound_listener_port = 10101 dialed_directly = true } - endpoint = { + destination = { address = "10.0.0.0/16", port = 443 } @@ -831,7 +831,7 @@ func TestParseConfigEntry(t *testing.T) { outbound_listener_port = 10101 dialed_directly = true } - Endpoint = { + Destination = { Address = "10.0.0.0/16", Port = 443 } @@ -853,7 +853,7 @@ func TestParseConfigEntry(t *testing.T) { "outbound_listener_port": 10101, "dialed_directly": true }, - "endpoint": { + "destination": { "address": "10.0.0.0/16", "port": 443 } @@ -876,7 +876,7 @@ func TestParseConfigEntry(t *testing.T) { "OutboundListenerPort": 10101, "DialedDirectly": true }, - "Endpoint": { + "Destination": { "Address": "10.0.0.0/16", "Port": 443 } @@ -898,7 +898,7 @@ func TestParseConfigEntry(t *testing.T) { OutboundListenerPort: 10101, DialedDirectly: true, }, - Endpoint: &api.EndpointConfig{ + Destination: &api.DestinationConfig{ Address: "10.0.0.0/16", Port: 443, },