From 423257c4736267fcca04c7d5ac27714d9998d843 Mon Sep 17 00:00:00 2001 From: Nathan Coleman Date: Wed, 8 Feb 2023 16:52:12 -0500 Subject: [PATCH] Implement APIGateway proxycfg snapshot (#16194) * Stub proxycfg handler for API gateway * Add Service Kind constants/handling for API Gateway * Begin stubbing for SDS * Add new Secret type to xDS order of operations * Continue stubbing of SDS * Iterate on proxycfg handler for API gateway * Handle BoundAPIGateway config entry subscription in proxycfg-glue * Add API gateway to config snapshot validation * Add API gateway to config snapshot clone, leaf, etc. * Subscribe to bound route + cert config entries on bound-api-gateway * Track routes + certs on API gateway config snapshot * Generate DeepCopy() for types used in watch.Map * Watch all active references on api-gateway, unwatch inactive * Track loading of initial bound-api-gateway config entry * Use proper proto package for SDS mapping * Use ResourceReference instead of ServiceName, collect resources * Fix typo, add + remove TODOs * Watch discovery chains for TCPRoute * Add TODO for updating gateway services for api-gateway * make proto * Regenerate deep-copy for proxycfg * Set datacenter on upstream ID from query source * Watch discovery chains for http-route service backends * Add ServiceName getter to HTTP+TCP Service structs * Clean up unwatched discovery chains on API Gateway * Implement watch for ingress leaf certificate * Collect upstreams on http-route + tcp-route updates * Remove unused GatewayServices update handler * Remove unnecessary gateway services logic for API Gateway * Remove outdate TODO * Use .ToIngress where appropriate, including TODO for cleaning up * Cancel before returning error * Remove GatewayServices subscription * Add godoc for handlerAPIGateway functions * Update terminology from Connect => Consul Service Mesh Consistent with terminology changes in https://github.com/hashicorp/consul/pull/12690 * Add missing TODO * Remove duplicate switch case * Rerun deep-copy generator * Use correct property on config snapshot * Remove unnecessary leaf cert watch * Clean up based on code review feedback * Note handler properties that are initialized but set elsewhere * Add TODO for moving helper func into structs pkg * Update generated DeepCopy code * gofmt * Generate DeepCopy() for API gateway listener types * Improve variable name * Regenerate DeepCopy() code * Fix linting issue * Temporarily remove the secret type from resource generation --- agent/config/builder.go | 2 + agent/consul/state/config_entry.go | 2 +- .../dataplane/get_envoy_bootstrap_params.go | 2 + agent/proxycfg/api_gateway.go | 426 ++++++++++++++++++ agent/proxycfg/deep-copy.sh | 1 + agent/proxycfg/proxycfg.deepcopy.go | 103 ++++- agent/proxycfg/snapshot.go | 87 ++++ agent/proxycfg/state.go | 4 + agent/proxycfg/upstreams.go | 10 + agent/structs/config_entry_gateways.go | 11 +- agent/structs/config_entry_routes.go | 8 + agent/structs/deep-copy.sh | 5 + agent/structs/structs.deepcopy.go | 170 ++++++- agent/structs/structs.go | 25 +- agent/xds/clusters.go | 8 + agent/xds/delta.go | 22 +- agent/xds/endpoints.go | 4 + agent/xds/listeners.go | 17 +- agent/xds/resources.go | 6 +- agent/xds/routes.go | 4 + agent/xds/secrets.go | 38 ++ agent/xds/server.go | 2 +- envoyextensions/xdscommon/xdscommon.go | 3 + proto-public/pbdataplane/dataplane.pb.go | 83 ++-- proto-public/pbdataplane/dataplane.proto | 4 + 25 files changed, 971 insertions(+), 76 deletions(-) create mode 100644 agent/proxycfg/api_gateway.go create mode 100644 agent/xds/secrets.go diff --git a/agent/config/builder.go b/agent/config/builder.go index 39d6e814f..aa25c0723 100644 --- a/agent/config/builder.go +++ b/agent/config/builder.go @@ -1715,6 +1715,8 @@ func (b *builder) serviceKindVal(v *string) structs.ServiceKind { return structs.ServiceKindTerminatingGateway case string(structs.ServiceKindIngressGateway): return structs.ServiceKindIngressGateway + case string(structs.ServiceKindAPIGateway): + return structs.ServiceKindAPIGateway default: return structs.ServiceKindTypical } diff --git a/agent/consul/state/config_entry.go b/agent/consul/state/config_entry.go index 2595f46f3..903fcbe9e 100644 --- a/agent/consul/state/config_entry.go +++ b/agent/consul/state/config_entry.go @@ -462,9 +462,9 @@ func insertConfigEntryWithTxn(tx WriteTxn, idx uint64, conf structs.ConfigEntry) if conf == nil { return fmt.Errorf("cannot insert nil config entry") } + // 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 { diff --git a/agent/grpc-external/services/dataplane/get_envoy_bootstrap_params.go b/agent/grpc-external/services/dataplane/get_envoy_bootstrap_params.go index 1d4cc6af9..b51a0910e 100644 --- a/agent/grpc-external/services/dataplane/get_envoy_bootstrap_params.go +++ b/agent/grpc-external/services/dataplane/get_envoy_bootstrap_params.go @@ -126,6 +126,8 @@ func convertToResponseServiceKind(serviceKind structs.ServiceKind) (respKind pbd respKind = pbdataplane.ServiceKind_SERVICE_KIND_TERMINATING_GATEWAY case structs.ServiceKindIngressGateway: respKind = pbdataplane.ServiceKind_SERVICE_KIND_INGRESS_GATEWAY + case structs.ServiceKindAPIGateway: + respKind = pbdataplane.ServiceKind_SERVICE_KIND_API_GATEWAY case structs.ServiceKindTypical: respKind = pbdataplane.ServiceKind_SERVICE_KIND_TYPICAL } diff --git a/agent/proxycfg/api_gateway.go b/agent/proxycfg/api_gateway.go new file mode 100644 index 000000000..c7c8c57ee --- /dev/null +++ b/agent/proxycfg/api_gateway.go @@ -0,0 +1,426 @@ +package proxycfg + +import ( + "context" + "fmt" + + "github.com/hashicorp/consul/agent/proxycfg/internal/watch" + "github.com/hashicorp/consul/agent/structs" + "github.com/hashicorp/consul/proto/pbpeering" +) + +var _ kindHandler = (*handlerAPIGateway)(nil) + +// handlerAPIGateway generates a new ConfigSnapshot in response to +// changes related to an api-gateway. +type handlerAPIGateway struct { + handlerState +} + +// initialize sets up the initial watches needed based on the api-gateway registration +func (h *handlerAPIGateway) initialize(ctx context.Context) (ConfigSnapshot, error) { + snap := newConfigSnapshotFromServiceInstance(h.serviceInstance, h.stateConfig) + + // Watch for root changes + err := h.dataSources.CARoots.Notify(ctx, &structs.DCSpecificRequest{ + Datacenter: h.source.Datacenter, + QueryOptions: structs.QueryOptions{Token: h.token}, + Source: *h.source, + }, rootsWatchID, h.ch) + if err != nil { + return snap, err + } + + // Get information about the entire service mesh. + err = h.dataSources.ConfigEntry.Notify(ctx, &structs.ConfigEntryQuery{ + Kind: structs.MeshConfig, + Name: structs.MeshConfigMesh, + Datacenter: h.source.Datacenter, + QueryOptions: structs.QueryOptions{Token: h.token}, + EnterpriseMeta: *structs.DefaultEnterpriseMetaInPartition(h.proxyID.PartitionOrDefault()), + }, meshConfigEntryID, h.ch) + if err != nil { + return snap, err + } + + // Watch the api-gateway's config entry + err = h.subscribeToConfigEntry(ctx, structs.APIGateway, h.service, gatewayConfigWatchID) + if err != nil { + return snap, err + } + + // Watch the bound-api-gateway's config entry + err = h.subscribeToConfigEntry(ctx, structs.BoundAPIGateway, h.service, gatewayConfigWatchID) + if err != nil { + return snap, err + } + + snap.APIGateway.Listeners = make(map[string]structs.APIGatewayListener) + snap.APIGateway.BoundListeners = make(map[string]structs.BoundAPIGatewayListener) + snap.APIGateway.HTTPRoutes = watch.NewMap[structs.ResourceReference, *structs.HTTPRouteConfigEntry]() + snap.APIGateway.TCPRoutes = watch.NewMap[structs.ResourceReference, *structs.TCPRouteConfigEntry]() + snap.APIGateway.Certificates = watch.NewMap[structs.ResourceReference, *structs.InlineCertificateConfigEntry]() + + // These need to be initialized here but are set by handlerUpstreams + snap.APIGateway.DiscoveryChain = make(map[UpstreamID]*structs.CompiledDiscoveryChain) + snap.APIGateway.PeerUpstreamEndpoints = watch.NewMap[UpstreamID, structs.CheckServiceNodes]() + snap.APIGateway.PeerUpstreamEndpointsUseHostnames = make(map[UpstreamID]struct{}) + snap.APIGateway.UpstreamPeerTrustBundles = watch.NewMap[string, *pbpeering.PeeringTrustBundle]() + snap.APIGateway.WatchedDiscoveryChains = make(map[UpstreamID]context.CancelFunc) + snap.APIGateway.WatchedGateways = make(map[UpstreamID]map[string]context.CancelFunc) + snap.APIGateway.WatchedGatewayEndpoints = make(map[UpstreamID]map[string]structs.CheckServiceNodes) + snap.APIGateway.WatchedUpstreams = make(map[UpstreamID]map[string]context.CancelFunc) + snap.APIGateway.WatchedUpstreamEndpoints = make(map[UpstreamID]map[string]structs.CheckServiceNodes) + + return snap, nil +} + +func (h *handlerAPIGateway) subscribeToConfigEntry(ctx context.Context, kind, name, watchID string) error { + return h.dataSources.ConfigEntry.Notify(ctx, &structs.ConfigEntryQuery{ + Kind: kind, + Name: name, + Datacenter: h.source.Datacenter, + QueryOptions: structs.QueryOptions{Token: h.token}, + EnterpriseMeta: h.proxyID.EnterpriseMeta, + }, watchID, h.ch) +} + +// handleUpdate responds to changes in the api-gateway. In general, we want +// to crawl the various resources related to or attached to the gateway and +// collect the list of things need to generate xDS. This list of resources +// includes the bound-api-gateway, http-routes, tcp-routes, and inline-certificates. +func (h *handlerAPIGateway) handleUpdate(ctx context.Context, u UpdateEvent, snap *ConfigSnapshot) error { + if u.Err != nil { + return fmt.Errorf("error filling agent cache: %v", u.Err) + } + + switch { + case u.CorrelationID == rootsWatchID: + // Handle change in the CA roots + if err := h.handleRootCAUpdate(u, snap); err != nil { + return err + } + case u.CorrelationID == gatewayConfigWatchID: + // Handle change in the api-gateway or bound-api-gateway config entry + if err := h.handleGatewayConfigUpdate(ctx, u, snap); err != nil { + return err + } + case u.CorrelationID == inlineCertificateConfigWatchID: + // Handle change in an attached inline-certificate config entry + if err := h.handleInlineCertConfigUpdate(ctx, u, snap); err != nil { + return err + } + case u.CorrelationID == routeConfigWatchID: + // Handle change in an attached http-route or tcp-route config entry + if err := h.handleRouteConfigUpdate(ctx, u, snap); err != nil { + return err + } + default: + return (*handlerUpstreams)(h).handleUpdateUpstreams(ctx, u, snap) + } + + return nil +} + +// handleRootCAUpdate responds to changes in the watched root CA for a gateway +func (h *handlerAPIGateway) handleRootCAUpdate(u UpdateEvent, snap *ConfigSnapshot) error { + roots, ok := u.Result.(*structs.IndexedCARoots) + if !ok { + return fmt.Errorf("invalid type for response: %T", u.Result) + } + snap.Roots = roots + return nil +} + +// handleGatewayConfigUpdate responds to changes in the watched config entry for a gateway. +// In particular, we want to make sure that we're subscribing to any attached resources such +// as routes and certificates. These additional subscriptions will enable us to update the +// config snapshot appropriately for any route or certificate changes. +func (h *handlerAPIGateway) handleGatewayConfigUpdate(ctx context.Context, u UpdateEvent, snap *ConfigSnapshot) error { + resp, ok := u.Result.(*structs.ConfigEntryResponse) + if !ok { + return fmt.Errorf("invalid type for response: %T", u.Result) + } else if resp.Entry == nil { + return nil + } + + switch gwConf := resp.Entry.(type) { + case *structs.BoundAPIGatewayConfigEntry: + snap.APIGateway.BoundGatewayConfig = gwConf + + seenRefs := make(map[structs.ResourceReference]any) + for _, listener := range gwConf.Listeners { + snap.APIGateway.BoundListeners[listener.Name] = listener + + // Subscribe to changes in each attached x-route config entry + for _, ref := range listener.Routes { + seenRefs[ref] = struct{}{} + + ctx, cancel := context.WithCancel(ctx) + switch ref.Kind { + case structs.HTTPRoute: + snap.APIGateway.HTTPRoutes.InitWatch(ref, cancel) + case structs.TCPRoute: + snap.APIGateway.TCPRoutes.InitWatch(ref, cancel) + default: + cancel() + return fmt.Errorf("unexpected route kind on gateway: %s", ref.Kind) + } + + err := h.subscribeToConfigEntry(ctx, ref.Kind, ref.Name, routeConfigWatchID) + if err != nil { + // TODO May want to continue + return err + } + } + + // Subscribe to changes in each attached inline-certificate config entry + for _, ref := range listener.Certificates { + ctx, cancel := context.WithCancel(ctx) + seenRefs[ref] = struct{}{} + snap.APIGateway.Certificates.InitWatch(ref, cancel) + + err := h.subscribeToConfigEntry(ctx, ref.Kind, ref.Name, inlineCertificateConfigWatchID) + if err != nil { + // TODO May want to continue + return err + } + } + } + + // Unsubscribe from any config entries that are no longer attached + snap.APIGateway.HTTPRoutes.ForEachKey(func(ref structs.ResourceReference) bool { + if _, ok := seenRefs[ref]; !ok { + snap.APIGateway.HTTPRoutes.CancelWatch(ref) + } + return true + }) + + snap.APIGateway.TCPRoutes.ForEachKey(func(ref structs.ResourceReference) bool { + if _, ok := seenRefs[ref]; !ok { + snap.APIGateway.TCPRoutes.CancelWatch(ref) + } + return true + }) + + snap.APIGateway.Certificates.ForEachKey(func(ref structs.ResourceReference) bool { + if _, ok := seenRefs[ref]; !ok { + snap.APIGateway.Certificates.CancelWatch(ref) + } + return true + }) + + snap.APIGateway.BoundGatewayConfigLoaded = true + break + case *structs.APIGatewayConfigEntry: + snap.APIGateway.GatewayConfig = gwConf + + for _, listener := range gwConf.Listeners { + snap.APIGateway.Listeners[listener.Name] = listener + } + + snap.APIGateway.GatewayConfigLoaded = true + break + default: + return fmt.Errorf("invalid type for config entry: %T", resp.Entry) + } + + return nil +} + +// handleInlineCertConfigUpdate stores the certificate for the gateway +func (h *handlerAPIGateway) handleInlineCertConfigUpdate(_ context.Context, u UpdateEvent, snap *ConfigSnapshot) error { + resp, ok := u.Result.(*structs.ConfigEntryResponse) + if !ok { + return fmt.Errorf("invalid type for response: %T", u.Result) + } else if resp.Entry == nil { + return nil + } + + cfg, ok := resp.Entry.(*structs.InlineCertificateConfigEntry) + if !ok { + return fmt.Errorf("invalid type for config entry: %T", resp.Entry) + } + + // TODO Consider if unset SectionName and acl.EnterpriseMeta could trip us up + ref := structs.ResourceReference{ + Kind: cfg.GetKind(), + Name: cfg.GetName(), + } + + snap.APIGateway.Certificates.Set(ref, cfg) + + return nil +} + +// handleRouteConfigUpdate builds the list of upstreams for services on +// the route and watches the related discovery chains. +func (h *handlerAPIGateway) handleRouteConfigUpdate(ctx context.Context, u UpdateEvent, snap *ConfigSnapshot) error { + resp, ok := u.Result.(*structs.ConfigEntryResponse) + if !ok { + return fmt.Errorf("invalid type for response: %T", u.Result) + } else if resp.Entry == nil { + return nil + } + + // TODO Consider if unset SectionName and acl.EnterpriseMeta could trip us up + ref := structs.ResourceReference{ + Kind: resp.Entry.GetKind(), + Name: resp.Entry.GetName(), + } + + seenUpstreamIDs := make(map[UpstreamID]struct{}) + upstreams := make(map[APIGatewayListenerKey]structs.Upstreams) + + switch route := resp.Entry.(type) { + case *structs.HTTPRouteConfigEntry: + snap.APIGateway.HTTPRoutes.Set(ref, route) + + for _, rule := range route.Rules { + for _, service := range rule.Services { + for _, listener := range snap.APIGateway.Listeners { + shouldBind := false + for _, parent := range route.Parents { + if h.referenceIsForListener(parent, listener, snap) { + shouldBind = true + break + } + } + if !shouldBind { + continue + } + + upstream := structs.Upstream{ + DestinationName: service.Name, + DestinationNamespace: service.NamespaceOrDefault(), + DestinationPartition: service.PartitionOrDefault(), + LocalBindPort: listener.Port, + // TODO IngressHosts: g.Hosts, + // Pass the protocol that was configured on the listener in order + // to force that protocol on the Envoy listener. + Config: map[string]interface{}{ + "protocol": "http", + }, + } + + listenerKey := APIGatewayListenerKey{Protocol: string(listener.Protocol), Port: listener.Port} + upstreams[listenerKey] = append(upstreams[listenerKey], upstream) + } + + upstreamID := NewUpstreamIDFromServiceName(service.ServiceName()) + seenUpstreamIDs[upstreamID] = struct{}{} + + watchOpts := discoveryChainWatchOpts{ + id: upstreamID, + name: service.Name, + namespace: service.NamespaceOrDefault(), + partition: service.PartitionOrDefault(), + datacenter: h.stateConfig.source.Datacenter, + } + + handler := &handlerUpstreams{handlerState: h.handlerState} + if err := handler.watchDiscoveryChain(ctx, snap, watchOpts); err != nil { + return fmt.Errorf("failed to watch discovery chain for %s: %w", upstreamID, err) + } + } + } + + case *structs.TCPRouteConfigEntry: + snap.APIGateway.TCPRoutes.Set(ref, route) + + for _, service := range route.Services { + upstreamID := NewUpstreamIDFromServiceName(service.ServiceName()) + seenUpstreamIDs[upstreamID] = struct{}{} + + // For each listener, check if this route should bind and, if so, create an upstream. + for _, listener := range snap.APIGateway.Listeners { + shouldBind := false + for _, parent := range route.Parents { + if h.referenceIsForListener(parent, listener, snap) { + shouldBind = true + break + } + } + if !shouldBind { + continue + } + + upstream := structs.Upstream{ + DestinationName: service.Name, + DestinationNamespace: service.NamespaceOrDefault(), + DestinationPartition: service.PartitionOrDefault(), + LocalBindPort: listener.Port, + //IngressHosts: g.Hosts, + // Pass the protocol that was configured on the ingress listener in order + // to force that protocol on the Envoy listener. + Config: map[string]interface{}{ + "protocol": "tcp", + }, + } + + listenerKey := APIGatewayListenerKey{Protocol: string(listener.Protocol), Port: listener.Port} + upstreams[listenerKey] = append(upstreams[listenerKey], upstream) + } + + watchOpts := discoveryChainWatchOpts{ + id: upstreamID, + name: service.Name, + namespace: service.NamespaceOrDefault(), + partition: service.PartitionOrDefault(), + datacenter: h.stateConfig.source.Datacenter, + } + + handler := &handlerUpstreams{handlerState: h.handlerState} + if err := handler.watchDiscoveryChain(ctx, snap, watchOpts); err != nil { + return fmt.Errorf("failed to watch discovery chain for %s: %w", upstreamID, err) + } + } + default: + return fmt.Errorf("invalid type for config entry: %T", resp.Entry) + } + + snap.APIGateway.Upstreams = upstreams + snap.APIGateway.UpstreamsSet = seenUpstreamIDs + //snap.APIGateway.Hosts = TODO + snap.APIGateway.AreHostsSet = true + + // Stop watching any upstreams and discovery chains that have become irrelevant + for upstreamID, cancelDiscoChain := range snap.APIGateway.WatchedDiscoveryChains { + if _, ok := seenUpstreamIDs[upstreamID]; ok { + continue + } + + for targetID, cancelUpstream := range snap.APIGateway.WatchedUpstreams[upstreamID] { + cancelUpstream() + delete(snap.APIGateway.WatchedUpstreams[upstreamID], targetID) + delete(snap.APIGateway.WatchedUpstreamEndpoints[upstreamID], targetID) + + if targetUID := NewUpstreamIDFromTargetID(targetID); targetUID.Peer != "" { + snap.APIGateway.PeerUpstreamEndpoints.CancelWatch(targetUID) + snap.APIGateway.UpstreamPeerTrustBundles.CancelWatch(targetUID.Peer) + } + } + + cancelDiscoChain() + delete(snap.APIGateway.WatchedDiscoveryChains, upstreamID) + } + + return nil +} + +// referenceIsForListener returns whether the provided structs.ResourceReference +// targets the provided structs.APIGatewayListener. For this to be true, the kind +// and name must match the structs.APIGatewayConfigEntry containing the listener, +// and the reference must specify either no section name or the name of the listener +// as the section name. +// +// TODO This would probably be more generally useful as a helper in the structs pkg +func (h *handlerAPIGateway) referenceIsForListener(ref structs.ResourceReference, listener structs.APIGatewayListener, snap *ConfigSnapshot) bool { + if ref.Kind != snap.APIGateway.GatewayConfig.Kind { + return false + } + if ref.Name != snap.APIGateway.GatewayConfig.Name { + return false + } + return ref.SectionName == "" || ref.SectionName == listener.Name +} diff --git a/agent/proxycfg/deep-copy.sh b/agent/proxycfg/deep-copy.sh index 48a9148d5..3e3f07ec2 100755 --- a/agent/proxycfg/deep-copy.sh +++ b/agent/proxycfg/deep-copy.sh @@ -10,6 +10,7 @@ deep-copy -pointer-receiver \ -type ConfigSnapshotUpstreams \ -type PeerServersValue \ -type PeeringServiceValue \ + -type configSnapshotAPIGateway \ -type configSnapshotConnectProxy \ -type configSnapshotIngressGateway \ -type configSnapshotMeshGateway \ diff --git a/agent/proxycfg/proxycfg.deepcopy.go b/agent/proxycfg/proxycfg.deepcopy.go index 121f1b0b1..3f4804a1c 100644 --- a/agent/proxycfg/proxycfg.deepcopy.go +++ b/agent/proxycfg/proxycfg.deepcopy.go @@ -1,4 +1,4 @@ -// generated by deep-copy -pointer-receiver -o ./proxycfg.deepcopy.go -type ConfigSnapshot -type ConfigSnapshotUpstreams -type PeerServersValue -type PeeringServiceValue -type configSnapshotConnectProxy -type configSnapshotIngressGateway -type configSnapshotMeshGateway -type configSnapshotTerminatingGateway ./; DO NOT EDIT. +// generated by deep-copy -pointer-receiver -o ./proxycfg.deepcopy.go -type ConfigSnapshot -type ConfigSnapshotUpstreams -type PeerServersValue -type PeeringServiceValue -type configSnapshotAPIGateway -type configSnapshotConnectProxy -type configSnapshotIngressGateway -type configSnapshotMeshGateway -type configSnapshotTerminatingGateway ./; DO NOT EDIT. package proxycfg @@ -47,6 +47,10 @@ func (o *ConfigSnapshot) DeepCopy() *ConfigSnapshot { retV := o.IngressGateway.DeepCopy() cp.IngressGateway = *retV } + { + retV := o.APIGateway.DeepCopy() + cp.APIGateway = *retV + } return &cp } @@ -209,6 +213,103 @@ func (o *PeeringServiceValue) DeepCopy() *PeeringServiceValue { return &cp } +// DeepCopy generates a deep copy of *configSnapshotAPIGateway +func (o *configSnapshotAPIGateway) DeepCopy() *configSnapshotAPIGateway { + var cp configSnapshotAPIGateway = *o + { + retV := o.ConfigSnapshotUpstreams.DeepCopy() + cp.ConfigSnapshotUpstreams = *retV + } + if o.TLSConfig.SDS != nil { + cp.TLSConfig.SDS = new(structs.GatewayTLSSDSConfig) + *cp.TLSConfig.SDS = *o.TLSConfig.SDS + } + if o.TLSConfig.CipherSuites != nil { + cp.TLSConfig.CipherSuites = make([]types.TLSCipherSuite, len(o.TLSConfig.CipherSuites)) + copy(cp.TLSConfig.CipherSuites, o.TLSConfig.CipherSuites) + } + if o.GatewayConfig != nil { + cp.GatewayConfig = new(structs.APIGatewayConfigEntry) + *cp.GatewayConfig = *o.GatewayConfig + if o.GatewayConfig.Listeners != nil { + cp.GatewayConfig.Listeners = make([]structs.APIGatewayListener, len(o.GatewayConfig.Listeners)) + copy(cp.GatewayConfig.Listeners, o.GatewayConfig.Listeners) + for i4 := range o.GatewayConfig.Listeners { + { + retV := o.GatewayConfig.Listeners[i4].DeepCopy() + cp.GatewayConfig.Listeners[i4] = *retV + } + } + } + { + retV := o.GatewayConfig.Status.DeepCopy() + cp.GatewayConfig.Status = *retV + } + if o.GatewayConfig.Meta != nil { + cp.GatewayConfig.Meta = make(map[string]string, len(o.GatewayConfig.Meta)) + for k4, v4 := range o.GatewayConfig.Meta { + cp.GatewayConfig.Meta[k4] = v4 + } + } + } + if o.BoundGatewayConfig != nil { + cp.BoundGatewayConfig = o.BoundGatewayConfig.DeepCopy() + } + if o.Hosts != nil { + cp.Hosts = make([]string, len(o.Hosts)) + copy(cp.Hosts, o.Hosts) + } + if o.Upstreams != nil { + cp.Upstreams = make(map[IngressListenerKey]structs.Upstreams, len(o.Upstreams)) + for k2, v2 := range o.Upstreams { + var cp_Upstreams_v2 structs.Upstreams + if v2 != nil { + cp_Upstreams_v2 = make([]structs.Upstream, len(v2)) + copy(cp_Upstreams_v2, v2) + for i3 := range v2 { + { + retV := v2[i3].DeepCopy() + cp_Upstreams_v2[i3] = *retV + } + } + } + cp.Upstreams[k2] = cp_Upstreams_v2 + } + } + if o.UpstreamsSet != nil { + cp.UpstreamsSet = make(map[UpstreamID]struct{}, len(o.UpstreamsSet)) + for k2, v2 := range o.UpstreamsSet { + cp.UpstreamsSet[k2] = v2 + } + } + cp.HTTPRoutes = o.HTTPRoutes.DeepCopy() + cp.TCPRoutes = o.TCPRoutes.DeepCopy() + cp.Certificates = o.Certificates.DeepCopy() + if o.Listeners != nil { + cp.Listeners = make(map[string]structs.APIGatewayListener, len(o.Listeners)) + for k2, v2 := range o.Listeners { + var cp_Listeners_v2 structs.APIGatewayListener + { + retV := v2.DeepCopy() + cp_Listeners_v2 = *retV + } + cp.Listeners[k2] = cp_Listeners_v2 + } + } + if o.BoundListeners != nil { + cp.BoundListeners = make(map[string]structs.BoundAPIGatewayListener, len(o.BoundListeners)) + for k2, v2 := range o.BoundListeners { + var cp_BoundListeners_v2 structs.BoundAPIGatewayListener + { + retV := v2.DeepCopy() + cp_BoundListeners_v2 = *retV + } + cp.BoundListeners[k2] = cp_BoundListeners_v2 + } + } + return &cp +} + // DeepCopy generates a deep copy of *configSnapshotConnectProxy func (o *configSnapshotConnectProxy) DeepCopy() *configSnapshotConnectProxy { var cp configSnapshotConnectProxy = *o diff --git a/agent/proxycfg/snapshot.go b/agent/proxycfg/snapshot.go index 15d9aef70..861fd0a07 100644 --- a/agent/proxycfg/snapshot.go +++ b/agent/proxycfg/snapshot.go @@ -639,6 +639,65 @@ func (c *configSnapshotMeshGateway) isEmptyPeering() bool { !c.PeeringTrustBundlesSet } +type configSnapshotAPIGateway struct { + ConfigSnapshotUpstreams + + TLSConfig structs.GatewayTLSConfig + + // GatewayConfigLoaded is used to determine if we have received the initial + // api-gateway config entry yet. + GatewayConfigLoaded bool + GatewayConfig *structs.APIGatewayConfigEntry + + // BoundGatewayConfigLoaded is used to determine if we have received the initial + // bound-api-gateway config entry yet. + BoundGatewayConfigLoaded bool + BoundGatewayConfig *structs.BoundAPIGatewayConfigEntry + + // Hosts is the list of extra host entries to add to our leaf cert's DNS SANs + Hosts []string + AreHostsSet bool + + // LeafCertWatchCancel is a CancelFunc to use when refreshing this gateway's + // leaf cert watch with different parameters. + //LeafCertWatchCancel context.CancelFunc + + // Upstreams is a list of upstreams this ingress gateway should serve traffic + // to. This is constructed from the ingress-gateway config entry, and uses + // the GatewayServices RPC to retrieve them. + // TODO Determine if this is updated "for free" or not. If not, we might need + // to do some work to populate it in handlerAPIGateway + Upstreams map[IngressListenerKey]structs.Upstreams + + // UpstreamsSet is the unique set of UpstreamID the gateway routes to. + UpstreamsSet map[UpstreamID]struct{} + + HTTPRoutes watch.Map[structs.ResourceReference, *structs.HTTPRouteConfigEntry] + TCPRoutes watch.Map[structs.ResourceReference, *structs.TCPRouteConfigEntry] + Certificates watch.Map[structs.ResourceReference, *structs.InlineCertificateConfigEntry] + + // Listeners is the original listener config from the api-gateway config + // entry to save us trying to pass fields through Upstreams + Listeners map[string]structs.APIGatewayListener + + BoundListeners map[string]structs.BoundAPIGatewayListener +} + +// ToIngress converts a configSnapshotAPIGateway to a configSnapshotIngressGateway. +// This is temporary, for the sake of re-using existing codepaths when integrating +// Consul API Gateway into Consul core. +// +// FUTURE: Remove when API gateways have custom snapshot generation +func (c *configSnapshotAPIGateway) ToIngress() configSnapshotIngressGateway { + + return configSnapshotIngressGateway{ + ConfigSnapshotUpstreams: c.ConfigSnapshotUpstreams, + // TODO Build from c.Listeners + // Listeners: + Defaults: structs.IngressServiceConfig{}, + } +} + type configSnapshotIngressGateway struct { ConfigSnapshotUpstreams @@ -687,6 +746,8 @@ func (c *configSnapshotIngressGateway) isEmpty() bool { !c.MeshConfigSet } +type APIGatewayListenerKey = IngressListenerKey + type IngressListenerKey struct { Protocol string Port int @@ -734,6 +795,9 @@ type ConfigSnapshot struct { // ingress-gateway specific IngressGateway configSnapshotIngressGateway + + // api-gateway specific + APIGateway configSnapshotAPIGateway } // Valid returns whether or not the snapshot has all required fields filled yet. @@ -773,6 +837,14 @@ func (s *ConfigSnapshot) Valid() bool { s.IngressGateway.GatewayConfigLoaded && s.IngressGateway.HostsSet && s.IngressGateway.MeshConfigSet + + case structs.ServiceKindAPIGateway: + // TODO Is this the proper set of things to validate? + return s.Roots != nil && + s.APIGateway.GatewayConfigLoaded && + s.APIGateway.BoundGatewayConfigLoaded && + s.APIGateway.AreHostsSet && + s.APIGateway.MeshConfigSet default: return false } @@ -806,6 +878,15 @@ func (s *ConfigSnapshot) Clone() *ConfigSnapshot { snap.IngressGateway.WatchedDiscoveryChains = nil // only ingress-gateway snap.IngressGateway.LeafCertWatchCancel = nil + case structs.ServiceKindAPIGateway: + // common with connect-proxy and api-gateway + snap.APIGateway.WatchedUpstreams = nil + snap.APIGateway.WatchedGateways = nil + snap.APIGateway.WatchedDiscoveryChains = nil + + // only api-gateway + //snap.APIGateway.LeafCertWatchCancel = nil + //snap.APIGateway. } return snap @@ -817,6 +898,8 @@ func (s *ConfigSnapshot) Leaf() *structs.IssuedCert { return s.ConnectProxy.Leaf case structs.ServiceKindIngressGateway: return s.IngressGateway.Leaf + case structs.ServiceKindAPIGateway: + return s.APIGateway.Leaf case structs.ServiceKindMeshGateway: return s.MeshGateway.Leaf default: @@ -850,6 +933,8 @@ func (s *ConfigSnapshot) MeshConfig() *structs.MeshConfigEntry { return s.ConnectProxy.MeshConfig case structs.ServiceKindIngressGateway: return s.IngressGateway.MeshConfig + case structs.ServiceKindAPIGateway: + return s.APIGateway.MeshConfig case structs.ServiceKindTerminatingGateway: return s.TerminatingGateway.MeshConfig case structs.ServiceKindMeshGateway: @@ -881,6 +966,8 @@ func (s *ConfigSnapshot) ToConfigSnapshotUpstreams() (*ConfigSnapshotUpstreams, return &s.ConnectProxy.ConfigSnapshotUpstreams, nil case structs.ServiceKindIngressGateway: return &s.IngressGateway.ConfigSnapshotUpstreams, nil + case structs.ServiceKindAPIGateway: + return &s.APIGateway.ConfigSnapshotUpstreams, nil default: // This is a coherence check and should never fail return nil, fmt.Errorf("No upstream snapshot for gateway mode %q", s.Kind) diff --git a/agent/proxycfg/state.go b/agent/proxycfg/state.go index a296698a1..57964d54f 100644 --- a/agent/proxycfg/state.go +++ b/agent/proxycfg/state.go @@ -32,6 +32,8 @@ const ( serviceResolversWatchID = "service-resolvers" gatewayServicesWatchID = "gateway-services" gatewayConfigWatchID = "gateway-config" + inlineCertificateConfigWatchID = "inline-certificate-config" + routeConfigWatchID = "route-config" externalServiceIDPrefix = "external-service:" serviceLeafIDPrefix = "service-leaf:" serviceConfigIDPrefix = "service-config:" @@ -198,6 +200,8 @@ func newKindHandler(config stateConfig, s serviceInstance, ch chan UpdateEvent) handler = &handlerMeshGateway{handlerState: h} case structs.ServiceKindIngressGateway: handler = &handlerIngressGateway{handlerState: h} + case structs.ServiceKindAPIGateway: + handler = &handlerAPIGateway{handlerState: h} default: return nil, errors.New("not a connect-proxy, terminating-gateway, mesh-gateway, or ingress-gateway") } diff --git a/agent/proxycfg/upstreams.go b/agent/proxycfg/upstreams.go index 71131117e..e2c6f137a 100644 --- a/agent/proxycfg/upstreams.go +++ b/agent/proxycfg/upstreams.go @@ -63,6 +63,14 @@ func (s *handlerUpstreams) handleUpdateUpstreams(ctx context.Context, u UpdateEv uid := UpstreamIDFromString(uidString) switch snap.Kind { + case structs.ServiceKindAPIGateway: + if _, ok := snap.APIGateway.UpstreamsSet[uid]; !ok { + // Discovery chain is not associated with a known explicit or implicit upstream so it is purged/skipped. + // The associated watch was likely cancelled. + delete(upstreamsSnapshot.DiscoveryChain, uid) + s.logger.Trace("discovery-chain watch fired for unknown upstream", "upstream", uid) + return nil + } case structs.ServiceKindIngressGateway: if _, ok := snap.IngressGateway.UpstreamsSet[uid]; !ok { // Discovery chain is not associated with a known explicit or implicit upstream so it is purged/skipped. @@ -533,6 +541,8 @@ type discoveryChainWatchOpts struct { func (s *handlerUpstreams) watchDiscoveryChain(ctx context.Context, snap *ConfigSnapshot, opts discoveryChainWatchOpts) error { var watchedDiscoveryChains map[UpstreamID]context.CancelFunc switch s.kind { + case structs.ServiceKindAPIGateway: + watchedDiscoveryChains = snap.APIGateway.WatchedDiscoveryChains case structs.ServiceKindIngressGateway: watchedDiscoveryChains = snap.IngressGateway.WatchedDiscoveryChains case structs.ServiceKindConnectProxy: diff --git a/agent/structs/config_entry_gateways.go b/agent/structs/config_entry_gateways.go index 1096e7748..6757775b4 100644 --- a/agent/structs/config_entry_gateways.go +++ b/agent/structs/config_entry_gateways.go @@ -704,6 +704,7 @@ type APIGatewayConfigEntry struct { // Listeners is the set of listener configuration to which an API Gateway // might bind. Listeners []APIGatewayListener + // Status is the asynchronous status which an APIGateway propagates to the user. Status Status @@ -862,16 +863,16 @@ const ( // APIGatewayListener represents an individual listener for an APIGateway type APIGatewayListener struct { // Name is the optional name of the listener in a given gateway. This is - // optional, however, it must be unique. Therefore, if a gateway has more - // than a single listener, all but one must specify a Name. + // optional but must be unique within a gateway; therefore, if a gateway + // has more than a single listener, all but one must specify a Name. Name string - // Hostname is the host name that a listener should be bound to, if + // Hostname is the host name that a listener should be bound to. If // unspecified, the listener accepts requests for all hostnames. Hostname string // Port is the port at which this listener should bind. Port int - // Protocol is the protocol that a listener should use, it must - // either be http or tcp + // Protocol is the protocol that a listener should use. It must + // either be http or tcp. Protocol APIGatewayListenerProtocol // TLS is the TLS settings for the listener. TLS APIGatewayTLSConfiguration diff --git a/agent/structs/config_entry_routes.go b/agent/structs/config_entry_routes.go index 8cd058f67..44950c2c9 100644 --- a/agent/structs/config_entry_routes.go +++ b/agent/structs/config_entry_routes.go @@ -244,6 +244,10 @@ type HTTPService struct { acl.EnterpriseMeta } +func (s HTTPService) ServiceName() ServiceName { + return NewServiceName(s.Name, &s.EnterpriseMeta) +} + var _ ControlledConfigEntry = (*HTTPRouteConfigEntry)(nil) func (e *HTTPRouteConfigEntry) GetStatus() Status { @@ -398,3 +402,7 @@ type TCPService struct { acl.EnterpriseMeta } + +func (s TCPService) ServiceName() ServiceName { + return NewServiceName(s.Name, &s.EnterpriseMeta) +} diff --git a/agent/structs/deep-copy.sh b/agent/structs/deep-copy.sh index a286340ad..f0e5c91fb 100755 --- a/agent/structs/deep-copy.sh +++ b/agent/structs/deep-copy.sh @@ -7,6 +7,8 @@ cd $PACKAGE_DIR deep-copy \ -pointer-receiver \ -o ./structs.deepcopy.go \ + -type APIGatewayListener \ + -type BoundAPIGatewayListener \ -type CARoot \ -type CheckServiceNode \ -type CheckType \ @@ -21,10 +23,12 @@ deep-copy \ -type GatewayService \ -type GatewayServiceTLSConfig \ -type HTTPHeaderModifiers \ + -type HTTPRouteConfigEntry \ -type HashPolicy \ -type HealthCheck \ -type IndexedCARoots \ -type IngressListener \ + -type InlineCertificateConfigEntry \ -type Intention \ -type IntentionPermission \ -type LoadBalancer \ @@ -43,6 +47,7 @@ deep-copy \ -type ServiceRoute \ -type ServiceRouteDestination \ -type ServiceRouteMatch \ + -type TCPRouteConfigEntry \ -type Upstream \ -type UpstreamConfiguration \ -type Status \ diff --git a/agent/structs/structs.deepcopy.go b/agent/structs/structs.deepcopy.go index 405e2f663..b7ce3f8de 100644 --- a/agent/structs/structs.deepcopy.go +++ b/agent/structs/structs.deepcopy.go @@ -1,4 +1,4 @@ -// generated by deep-copy -pointer-receiver -o ./structs.deepcopy.go -type CARoot -type CheckServiceNode -type CheckType -type CompiledDiscoveryChain -type ConnectProxyConfig -type DiscoveryFailover -type DiscoveryGraphNode -type DiscoveryResolver -type DiscoveryRoute -type DiscoverySplit -type ExposeConfig -type GatewayService -type GatewayServiceTLSConfig -type HTTPHeaderModifiers -type HashPolicy -type HealthCheck -type IndexedCARoots -type IngressListener -type Intention -type IntentionPermission -type LoadBalancer -type MeshConfigEntry -type MeshDirectionalTLSConfig -type MeshTLSConfig -type Node -type NodeService -type PeeringServiceMeta -type ServiceConfigEntry -type ServiceConfigResponse -type ServiceConnect -type ServiceDefinition -type ServiceResolverConfigEntry -type ServiceResolverFailover -type ServiceRoute -type ServiceRouteDestination -type ServiceRouteMatch -type Upstream -type UpstreamConfiguration -type Status -type BoundAPIGatewayConfigEntry ./; DO NOT EDIT. +// generated by deep-copy -pointer-receiver -o ./structs.deepcopy.go -type APIGatewayListener -type BoundAPIGatewayListener -type CARoot -type CheckServiceNode -type CheckType -type CompiledDiscoveryChain -type ConnectProxyConfig -type DiscoveryFailover -type DiscoveryGraphNode -type DiscoveryResolver -type DiscoveryRoute -type DiscoverySplit -type ExposeConfig -type GatewayService -type GatewayServiceTLSConfig -type HTTPHeaderModifiers -type HTTPRouteConfigEntry -type HashPolicy -type HealthCheck -type IndexedCARoots -type IngressListener -type InlineCertificateConfigEntry -type Intention -type IntentionPermission -type LoadBalancer -type MeshConfigEntry -type MeshDirectionalTLSConfig -type MeshTLSConfig -type Node -type NodeService -type PeeringServiceMeta -type ServiceConfigEntry -type ServiceConfigResponse -type ServiceConnect -type ServiceDefinition -type ServiceResolverConfigEntry -type ServiceResolverFailover -type ServiceRoute -type ServiceRouteDestination -type ServiceRouteMatch -type TCPRouteConfigEntry -type Upstream -type UpstreamConfiguration -type Status -type BoundAPIGatewayConfigEntry ./; DO NOT EDIT. package structs @@ -7,6 +7,34 @@ import ( "time" ) +// DeepCopy generates a deep copy of *APIGatewayListener +func (o *APIGatewayListener) DeepCopy() *APIGatewayListener { + var cp APIGatewayListener = *o + if o.TLS.Certificates != nil { + cp.TLS.Certificates = make([]ResourceReference, len(o.TLS.Certificates)) + copy(cp.TLS.Certificates, o.TLS.Certificates) + } + if o.TLS.CipherSuites != nil { + cp.TLS.CipherSuites = make([]types.TLSCipherSuite, len(o.TLS.CipherSuites)) + copy(cp.TLS.CipherSuites, o.TLS.CipherSuites) + } + return &cp +} + +// DeepCopy generates a deep copy of *BoundAPIGatewayListener +func (o *BoundAPIGatewayListener) DeepCopy() *BoundAPIGatewayListener { + var cp BoundAPIGatewayListener = *o + if o.Routes != nil { + cp.Routes = make([]ResourceReference, len(o.Routes)) + copy(cp.Routes, o.Routes) + } + if o.Certificates != nil { + cp.Certificates = make([]ResourceReference, len(o.Certificates)) + copy(cp.Certificates, o.Certificates) + } + return &cp +} + // DeepCopy generates a deep copy of *CARoot func (o *CARoot) DeepCopy() *CARoot { var cp CARoot = *o @@ -268,6 +296,100 @@ func (o *HTTPHeaderModifiers) DeepCopy() *HTTPHeaderModifiers { return &cp } +// DeepCopy generates a deep copy of *HTTPRouteConfigEntry +func (o *HTTPRouteConfigEntry) DeepCopy() *HTTPRouteConfigEntry { + var cp HTTPRouteConfigEntry = *o + if o.Parents != nil { + cp.Parents = make([]ResourceReference, len(o.Parents)) + copy(cp.Parents, o.Parents) + } + if o.Rules != nil { + cp.Rules = make([]HTTPRouteRule, len(o.Rules)) + copy(cp.Rules, o.Rules) + for i2 := range o.Rules { + if o.Rules[i2].Filters.Headers != nil { + cp.Rules[i2].Filters.Headers = make([]HTTPHeaderFilter, len(o.Rules[i2].Filters.Headers)) + copy(cp.Rules[i2].Filters.Headers, o.Rules[i2].Filters.Headers) + for i5 := range o.Rules[i2].Filters.Headers { + if o.Rules[i2].Filters.Headers[i5].Add != nil { + cp.Rules[i2].Filters.Headers[i5].Add = make(map[string]string, len(o.Rules[i2].Filters.Headers[i5].Add)) + for k7, v7 := range o.Rules[i2].Filters.Headers[i5].Add { + cp.Rules[i2].Filters.Headers[i5].Add[k7] = v7 + } + } + if o.Rules[i2].Filters.Headers[i5].Remove != nil { + cp.Rules[i2].Filters.Headers[i5].Remove = make([]string, len(o.Rules[i2].Filters.Headers[i5].Remove)) + copy(cp.Rules[i2].Filters.Headers[i5].Remove, o.Rules[i2].Filters.Headers[i5].Remove) + } + if o.Rules[i2].Filters.Headers[i5].Set != nil { + cp.Rules[i2].Filters.Headers[i5].Set = make(map[string]string, len(o.Rules[i2].Filters.Headers[i5].Set)) + for k7, v7 := range o.Rules[i2].Filters.Headers[i5].Set { + cp.Rules[i2].Filters.Headers[i5].Set[k7] = v7 + } + } + } + } + if o.Rules[i2].Matches != nil { + cp.Rules[i2].Matches = make([]HTTPMatch, len(o.Rules[i2].Matches)) + copy(cp.Rules[i2].Matches, o.Rules[i2].Matches) + for i4 := range o.Rules[i2].Matches { + if o.Rules[i2].Matches[i4].Headers != nil { + cp.Rules[i2].Matches[i4].Headers = make([]HTTPHeaderMatch, len(o.Rules[i2].Matches[i4].Headers)) + copy(cp.Rules[i2].Matches[i4].Headers, o.Rules[i2].Matches[i4].Headers) + } + if o.Rules[i2].Matches[i4].Query != nil { + cp.Rules[i2].Matches[i4].Query = make([]HTTPQueryMatch, len(o.Rules[i2].Matches[i4].Query)) + copy(cp.Rules[i2].Matches[i4].Query, o.Rules[i2].Matches[i4].Query) + } + } + } + if o.Rules[i2].Services != nil { + cp.Rules[i2].Services = make([]HTTPService, len(o.Rules[i2].Services)) + copy(cp.Rules[i2].Services, o.Rules[i2].Services) + for i4 := range o.Rules[i2].Services { + if o.Rules[i2].Services[i4].Filters.Headers != nil { + cp.Rules[i2].Services[i4].Filters.Headers = make([]HTTPHeaderFilter, len(o.Rules[i2].Services[i4].Filters.Headers)) + copy(cp.Rules[i2].Services[i4].Filters.Headers, o.Rules[i2].Services[i4].Filters.Headers) + for i7 := range o.Rules[i2].Services[i4].Filters.Headers { + if o.Rules[i2].Services[i4].Filters.Headers[i7].Add != nil { + cp.Rules[i2].Services[i4].Filters.Headers[i7].Add = make(map[string]string, len(o.Rules[i2].Services[i4].Filters.Headers[i7].Add)) + for k9, v9 := range o.Rules[i2].Services[i4].Filters.Headers[i7].Add { + cp.Rules[i2].Services[i4].Filters.Headers[i7].Add[k9] = v9 + } + } + if o.Rules[i2].Services[i4].Filters.Headers[i7].Remove != nil { + cp.Rules[i2].Services[i4].Filters.Headers[i7].Remove = make([]string, len(o.Rules[i2].Services[i4].Filters.Headers[i7].Remove)) + copy(cp.Rules[i2].Services[i4].Filters.Headers[i7].Remove, o.Rules[i2].Services[i4].Filters.Headers[i7].Remove) + } + if o.Rules[i2].Services[i4].Filters.Headers[i7].Set != nil { + cp.Rules[i2].Services[i4].Filters.Headers[i7].Set = make(map[string]string, len(o.Rules[i2].Services[i4].Filters.Headers[i7].Set)) + for k9, v9 := range o.Rules[i2].Services[i4].Filters.Headers[i7].Set { + cp.Rules[i2].Services[i4].Filters.Headers[i7].Set[k9] = v9 + } + } + } + } + } + } + } + } + if o.Hostnames != nil { + cp.Hostnames = make([]string, len(o.Hostnames)) + copy(cp.Hostnames, o.Hostnames) + } + if o.Meta != nil { + cp.Meta = make(map[string]string, len(o.Meta)) + for k2, v2 := range o.Meta { + cp.Meta[k2] = v2 + } + } + { + retV := o.Status.DeepCopy() + cp.Status = *retV + } + return &cp +} + // DeepCopy generates a deep copy of *HashPolicy func (o *HashPolicy) DeepCopy() *HashPolicy { var cp HashPolicy = *o @@ -369,6 +491,18 @@ func (o *IngressListener) DeepCopy() *IngressListener { return &cp } +// DeepCopy generates a deep copy of *InlineCertificateConfigEntry +func (o *InlineCertificateConfigEntry) DeepCopy() *InlineCertificateConfigEntry { + var cp InlineCertificateConfigEntry = *o + if o.Meta != nil { + cp.Meta = make(map[string]string, len(o.Meta)) + for k2, v2 := range o.Meta { + cp.Meta[k2] = v2 + } + } + return &cp +} + // DeepCopy generates a deep copy of *Intention func (o *Intention) DeepCopy() *Intention { var cp Intention = *o @@ -809,6 +943,30 @@ func (o *ServiceRouteMatch) DeepCopy() *ServiceRouteMatch { return &cp } +// DeepCopy generates a deep copy of *TCPRouteConfigEntry +func (o *TCPRouteConfigEntry) DeepCopy() *TCPRouteConfigEntry { + var cp TCPRouteConfigEntry = *o + if o.Parents != nil { + cp.Parents = make([]ResourceReference, len(o.Parents)) + copy(cp.Parents, o.Parents) + } + if o.Services != nil { + cp.Services = make([]TCPService, len(o.Services)) + copy(cp.Services, o.Services) + } + if o.Meta != nil { + cp.Meta = make(map[string]string, len(o.Meta)) + for k2, v2 := range o.Meta { + cp.Meta[k2] = v2 + } + } + { + retV := o.Status.DeepCopy() + cp.Status = *retV + } + return &cp +} + // DeepCopy generates a deep copy of *Upstream func (o *Upstream) DeepCopy() *Upstream { var cp Upstream = *o @@ -920,13 +1078,9 @@ func (o *BoundAPIGatewayConfigEntry) DeepCopy() *BoundAPIGatewayConfigEntry { cp.Listeners = make([]BoundAPIGatewayListener, len(o.Listeners)) copy(cp.Listeners, o.Listeners) for i2 := range o.Listeners { - if o.Listeners[i2].Routes != nil { - cp.Listeners[i2].Routes = make([]ResourceReference, len(o.Listeners[i2].Routes)) - copy(cp.Listeners[i2].Routes, o.Listeners[i2].Routes) - } - if o.Listeners[i2].Certificates != nil { - cp.Listeners[i2].Certificates = make([]ResourceReference, len(o.Listeners[i2].Certificates)) - copy(cp.Listeners[i2].Certificates, o.Listeners[i2].Certificates) + { + retV := o.Listeners[i2].DeepCopy() + cp.Listeners[i2] = *retV } } } diff --git a/agent/structs/structs.go b/agent/structs/structs.go index 5329526d2..1ae8bee8a 100644 --- a/agent/structs/structs.go +++ b/agent/structs/structs.go @@ -1193,7 +1193,8 @@ func (k ServiceKind) IsProxy() bool { case ServiceKindConnectProxy, ServiceKindMeshGateway, ServiceKindTerminatingGateway, - ServiceKindIngressGateway: + ServiceKindIngressGateway, + ServiceKindAPIGateway: return true } return false @@ -1206,26 +1207,31 @@ const ( // default to the typical service. ServiceKindTypical ServiceKind = "" - // ServiceKindConnectProxy is a proxy for the Connect feature. This + // ServiceKindConnectProxy is a proxy for the Consul Service Mesh. This // service proxies another service within Consul and speaks the connect // protocol. ServiceKindConnectProxy ServiceKind = "connect-proxy" - // ServiceKindMeshGateway is a Mesh Gateway for the Connect feature. This - // service will proxy connections based off the SNI header set by other + // ServiceKindMeshGateway is a Mesh Gateway for the Consul Service Mesh. + // This service will proxy connections based off the SNI header set by other // connect proxies ServiceKindMeshGateway ServiceKind = "mesh-gateway" - // ServiceKindTerminatingGateway is a Terminating Gateway for the Connect - // feature. This service will proxy connections to services outside the mesh. + // ServiceKindTerminatingGateway is a Terminating Gateway for the Consul Service + // Mesh feature. This service will proxy connections to services outside the mesh. ServiceKindTerminatingGateway ServiceKind = "terminating-gateway" - // ServiceKindIngressGateway is an Ingress Gateway for the Connect feature. + // ServiceKindIngressGateway is an Ingress Gateway for the Consul Service Mesh. // This service allows external traffic to enter the mesh based on // centralized configuration. ServiceKindIngressGateway ServiceKind = "ingress-gateway" - // ServiceKindDestination is a Destination for the Connect feature. + // ServiceKindAPIGateway is an API Gateway for the Consul Service Mesh. + // This service allows external traffic to enter the mesh based on + // centralized configuration. + ServiceKindAPIGateway ServiceKind = "api-gateway" + + // ServiceKindDestination is a Destination for the Consul Service Mesh feature. // This service allows external traffic to exit the mesh through a terminating gateway // based on centralized configuration. ServiceKindDestination ServiceKind = "destination" @@ -1424,7 +1430,8 @@ func (s *NodeService) IsSidecarProxy() bool { func (s *NodeService) IsGateway() bool { return s.Kind == ServiceKindMeshGateway || s.Kind == ServiceKindTerminatingGateway || - s.Kind == ServiceKindIngressGateway + s.Kind == ServiceKindIngressGateway || + s.Kind == ServiceKindAPIGateway } // Validate validates the node service configuration. diff --git a/agent/xds/clusters.go b/agent/xds/clusters.go index 35be5f598..4241ab7c0 100644 --- a/agent/xds/clusters.go +++ b/agent/xds/clusters.go @@ -61,6 +61,14 @@ func (s *ResourceGenerator) clustersFromSnapshot(cfgSnap *proxycfg.ConfigSnapsho return nil, err } return res, nil + case structs.ServiceKindAPIGateway: + // TODO Find a cleaner solution, can't currently pass unexported property types + cfgSnap.IngressGateway = cfgSnap.APIGateway.ToIngress() + res, err := s.clustersFromSnapshotIngressGateway(cfgSnap) + if err != nil { + return nil, err + } + return res, nil default: return nil, fmt.Errorf("Invalid service kind: %v", cfgSnap.Kind) } diff --git a/agent/xds/delta.go b/agent/xds/delta.go index 082fca3cc..5a1ef17d2 100644 --- a/agent/xds/delta.go +++ b/agent/xds/delta.go @@ -129,19 +129,24 @@ func (s *Server) processDelta(stream ADSDeltaStream, reqCh <-chan *envoy_discove // Configure handlers for each type of request we currently care about. handlers := map[string]*xDSDeltaType{ xdscommon.ListenerType: newDeltaType(generator, stream, xdscommon.ListenerType, func(kind structs.ServiceKind) bool { - return cfgSnap.Kind == structs.ServiceKindIngressGateway + // Ingress and API gateways are allowed to inform LDS of no listeners. + return cfgSnap.Kind == structs.ServiceKindIngressGateway || + cfgSnap.Kind == structs.ServiceKindAPIGateway }), xdscommon.RouteType: newDeltaType(generator, stream, xdscommon.RouteType, func(kind structs.ServiceKind) bool { - return cfgSnap.Kind == structs.ServiceKindIngressGateway + // Ingress and API gateways are allowed to inform RDS of no routes. + return cfgSnap.Kind == structs.ServiceKindIngressGateway || + cfgSnap.Kind == structs.ServiceKindAPIGateway }), xdscommon.ClusterType: newDeltaType(generator, stream, xdscommon.ClusterType, func(kind structs.ServiceKind) bool { - // Mesh, Ingress, and Terminating gateways are allowed to inform CDS of - // no clusters. + // Mesh, Ingress, API and Terminating gateways are allowed to inform CDS of no clusters. return cfgSnap.Kind == structs.ServiceKindMeshGateway || cfgSnap.Kind == structs.ServiceKindTerminatingGateway || - cfgSnap.Kind == structs.ServiceKindIngressGateway + cfgSnap.Kind == structs.ServiceKindIngressGateway || + cfgSnap.Kind == structs.ServiceKindAPIGateway }), xdscommon.EndpointType: newDeltaType(generator, stream, xdscommon.EndpointType, nil), + xdscommon.SecretType: newDeltaType(generator, stream, xdscommon.SecretType, nil), // TODO allowEmptyFn } // Endpoints are stored within a Cluster (and Routes @@ -332,7 +337,7 @@ func (s *Server) processDelta(stream ADSDeltaStream, reqCh <-chan *envoy_discove generator.Logger.Trace("Got initial config snapshot") - // Lets actually process the config we just got or we'll mis responding + // Let's actually process the config we just got, or we'll miss responding fallthrough case stateDeltaRunning: // Check ACLs on every Discovery{Request,Response}. @@ -462,6 +467,8 @@ func (s *Server) applyEnvoyExtensions(resources *xdscommon.IndexedResources, cfg } var xDSUpdateOrder = []xDSUpdateOperation{ + // TODO Update comments + {TypeUrl: xdscommon.SecretType, Upsert: true}, // 1. CDS updates (if any) must always be pushed first. {TypeUrl: xdscommon.ClusterType, Upsert: true}, // 2. EDS updates (if any) must arrive after CDS updates for the respective clusters. @@ -472,9 +479,10 @@ var xDSUpdateOrder = []xDSUpdateOperation{ {TypeUrl: xdscommon.RouteType, Upsert: true, Remove: true}, // 5. (NOT IMPLEMENTED YET IN CONSUL) VHDS updates (if any) related to the newly added RouteConfigurations must arrive after RDS updates. // {}, - // 6. Stale CDS clusters and related EDS endpoints (ones no longer being referenced) can then be removed. + // 6. Stale CDS clusters, related EDS endpoints (ones no longer being referenced) and SDS secrets can then be removed. {TypeUrl: xdscommon.ClusterType, Remove: true}, {TypeUrl: xdscommon.EndpointType, Remove: true}, + {TypeUrl: xdscommon.SecretType, Remove: true}, // xDS updates can be pushed independently if no new // clusters/routes/listeners are added or if it’s acceptable to // temporarily drop traffic during updates. Note that in case of diff --git a/agent/xds/endpoints.go b/agent/xds/endpoints.go index 364f9a674..d15d62c6d 100644 --- a/agent/xds/endpoints.go +++ b/agent/xds/endpoints.go @@ -37,6 +37,10 @@ func (s *ResourceGenerator) endpointsFromSnapshot(cfgSnap *proxycfg.ConfigSnapsh return s.endpointsFromSnapshotMeshGateway(cfgSnap) case structs.ServiceKindIngressGateway: return s.endpointsFromSnapshotIngressGateway(cfgSnap) + case structs.ServiceKindAPIGateway: + // TODO Find a cleaner solution, can't currently pass unexported property types + cfgSnap.IngressGateway = cfgSnap.APIGateway.ToIngress() + return s.endpointsFromSnapshotIngressGateway(cfgSnap) default: return nil, fmt.Errorf("Invalid service kind: %v", cfgSnap.Kind) } diff --git a/agent/xds/listeners.go b/agent/xds/listeners.go index a0268f078..c2b9b8bd6 100644 --- a/agent/xds/listeners.go +++ b/agent/xds/listeners.go @@ -58,11 +58,10 @@ func (s *ResourceGenerator) listenersFromSnapshot(cfgSnap *proxycfg.ConfigSnapsh switch cfgSnap.Kind { case structs.ServiceKindConnectProxy: return s.listenersFromSnapshotConnectProxy(cfgSnap) - case structs.ServiceKindTerminatingGateway: - return s.listenersFromSnapshotGateway(cfgSnap) - case structs.ServiceKindMeshGateway: - return s.listenersFromSnapshotGateway(cfgSnap) - case structs.ServiceKindIngressGateway: + case structs.ServiceKindTerminatingGateway, + structs.ServiceKindMeshGateway, + structs.ServiceKindIngressGateway, + structs.ServiceKindAPIGateway: return s.listenersFromSnapshotGateway(cfgSnap) default: return nil, fmt.Errorf("Invalid service kind: %v", cfgSnap.Kind) @@ -849,6 +848,14 @@ func (s *ResourceGenerator) listenersFromSnapshotGateway(cfgSnap *proxycfg.Confi if err != nil { return nil, err } + case structs.ServiceKindAPIGateway: + // TODO Find a cleaner solution, can't currently pass unexported property types + cfgSnap.IngressGateway = cfgSnap.APIGateway.ToIngress() + listeners, err := s.makeIngressGatewayListeners(a.Address, cfgSnap) + if err != nil { + return nil, err + } + resources = append(resources, listeners...) case structs.ServiceKindIngressGateway: listeners, err := s.makeIngressGatewayListeners(a.Address, cfgSnap) if err != nil { diff --git a/agent/xds/resources.go b/agent/xds/resources.go index dd5eee7f2..6bd670934 100644 --- a/agent/xds/resources.go +++ b/agent/xds/resources.go @@ -3,10 +3,11 @@ package xds import ( "fmt" - "github.com/hashicorp/consul/envoyextensions/xdscommon" "github.com/hashicorp/go-hclog" "google.golang.org/protobuf/proto" + "github.com/hashicorp/consul/envoyextensions/xdscommon" + "github.com/hashicorp/consul/agent/proxycfg" ) @@ -34,6 +35,7 @@ func NewResourceGenerator( func (g *ResourceGenerator) AllResourcesFromSnapshot(cfgSnap *proxycfg.ConfigSnapshot) (map[string][]proto.Message, error) { all := make(map[string][]proto.Message) + // TODO Add xdscommon.SecretType for _, typeUrl := range []string{xdscommon.ListenerType, xdscommon.RouteType, xdscommon.ClusterType, xdscommon.EndpointType} { res, err := g.resourcesFromSnapshot(typeUrl, cfgSnap) if err != nil { @@ -54,6 +56,8 @@ func (g *ResourceGenerator) resourcesFromSnapshot(typeUrl string, cfgSnap *proxy return g.clustersFromSnapshot(cfgSnap) case xdscommon.EndpointType: return g.endpointsFromSnapshot(cfgSnap) + case xdscommon.SecretType: + return g.secretsFromSnapshot(cfgSnap) default: return nil, fmt.Errorf("unknown typeUrl: %s", typeUrl) } diff --git a/agent/xds/routes.go b/agent/xds/routes.go index f5ac659e9..8fdbe5d69 100644 --- a/agent/xds/routes.go +++ b/agent/xds/routes.go @@ -32,6 +32,10 @@ func (s *ResourceGenerator) routesFromSnapshot(cfgSnap *proxycfg.ConfigSnapshot) return s.routesForConnectProxy(cfgSnap) case structs.ServiceKindIngressGateway: return s.routesForIngressGateway(cfgSnap) + case structs.ServiceKindAPIGateway: + // TODO Find a cleaner solution, can't currently pass unexported property types + cfgSnap.IngressGateway = cfgSnap.APIGateway.ToIngress() + return s.routesForIngressGateway(cfgSnap) case structs.ServiceKindTerminatingGateway: return s.routesForTerminatingGateway(cfgSnap) case structs.ServiceKindMeshGateway: diff --git a/agent/xds/secrets.go b/agent/xds/secrets.go new file mode 100644 index 000000000..5547b6d20 --- /dev/null +++ b/agent/xds/secrets.go @@ -0,0 +1,38 @@ +package xds + +import ( + "errors" + "fmt" + + "google.golang.org/protobuf/proto" + + "github.com/hashicorp/consul/agent/proxycfg" + "github.com/hashicorp/consul/agent/structs" +) + +// secretsFromSnapshot returns the xDS API representation of the "secrets" +// in the snapshot +func (s *ResourceGenerator) secretsFromSnapshot(cfgSnap *proxycfg.ConfigSnapshot) ([]proto.Message, error) { + if cfgSnap == nil { + return nil, errors.New("nil config given") + } + + switch cfgSnap.Kind { + case structs.ServiceKindConnectProxy, + structs.ServiceKindTerminatingGateway, + structs.ServiceKindMeshGateway, + structs.ServiceKindIngressGateway: + return nil, nil + // Only API gateways utilize secrets + case structs.ServiceKindAPIGateway: + return s.secretsFromSnapshotAPIGateway(cfgSnap) + default: + return nil, fmt.Errorf("Invalid service kind: %v", cfgSnap.Kind) + } +} + +func (s *ResourceGenerator) secretsFromSnapshotAPIGateway(cfgSnap *proxycfg.ConfigSnapshot) ([]proto.Message, error) { + var res []proto.Message + // TODO + return res, nil +} diff --git a/agent/xds/server.go b/agent/xds/server.go index 490b860f8..57d7224f8 100644 --- a/agent/xds/server.go +++ b/agent/xds/server.go @@ -229,7 +229,7 @@ func (s *Server) authorize(ctx context.Context, cfgSnap *proxycfg.ConfigSnapshot if err := authz.ToAllowAuthorizer().ServiceWriteAllowed(cfgSnap.Proxy.DestinationServiceName, &authzContext); err != nil { return status.Errorf(codes.PermissionDenied, err.Error()) } - case structs.ServiceKindMeshGateway, structs.ServiceKindTerminatingGateway, structs.ServiceKindIngressGateway: + case structs.ServiceKindMeshGateway, structs.ServiceKindTerminatingGateway, structs.ServiceKindIngressGateway, structs.ServiceKindAPIGateway: cfgSnap.ProxyID.EnterpriseMeta.FillAuthzContext(&authzContext) if err := authz.ToAllowAuthorizer().ServiceWriteAllowed(cfgSnap.Service, &authzContext); err != nil { return status.Errorf(codes.PermissionDenied, err.Error()) diff --git a/envoyextensions/xdscommon/xdscommon.go b/envoyextensions/xdscommon/xdscommon.go index 990cd0104..b1061fb10 100644 --- a/envoyextensions/xdscommon/xdscommon.go +++ b/envoyextensions/xdscommon/xdscommon.go @@ -46,6 +46,9 @@ const ( // ListenerType is the TypeURL for Listener discovery responses. ListenerType = apiTypePrefix + "envoy.config.listener.v3.Listener" + + // SecretType is the TypeURL for Secret discovery responses. + SecretType = apiTypePrefix + "envoy.extensions.transport_sockets.tls.v3.Secret" ) type IndexedResources struct { diff --git a/proto-public/pbdataplane/dataplane.pb.go b/proto-public/pbdataplane/dataplane.pb.go index c2c4515c6..c989f6df3 100644 --- a/proto-public/pbdataplane/dataplane.pb.go +++ b/proto-public/pbdataplane/dataplane.pb.go @@ -102,6 +102,9 @@ const ( // ServiceKind Ingress Gateway is an Ingress Gateway for the Connect feature. // This service will ingress connections into the service mesh. ServiceKind_SERVICE_KIND_INGRESS_GATEWAY ServiceKind = 5 + // ServiceKind API Gateway is an API Gateway for the Connect feature. + // This service will ingress connections in to the service mesh. + ServiceKind_SERVICE_KIND_API_GATEWAY ServiceKind = 6 ) // Enum value maps for ServiceKind. @@ -113,6 +116,7 @@ var ( 3: "SERVICE_KIND_MESH_GATEWAY", 4: "SERVICE_KIND_TERMINATING_GATEWAY", 5: "SERVICE_KIND_INGRESS_GATEWAY", + 6: "SERVICE_KIND_API_GATEWAY", } ServiceKind_value = map[string]int32{ "SERVICE_KIND_UNSPECIFIED": 0, @@ -121,6 +125,7 @@ var ( "SERVICE_KIND_MESH_GATEWAY": 3, "SERVICE_KIND_TERMINATING_GATEWAY": 4, "SERVICE_KIND_INGRESS_GATEWAY": 5, + "SERVICE_KIND_API_GATEWAY": 6, } ) @@ -593,7 +598,7 @@ var file_proto_public_pbdataplane_dataplane_proto_rawDesc = []byte{ 0x4e, 0x54, 0x10, 0x02, 0x12, 0x34, 0x0a, 0x30, 0x44, 0x41, 0x54, 0x41, 0x50, 0x4c, 0x41, 0x4e, 0x45, 0x5f, 0x46, 0x45, 0x41, 0x54, 0x55, 0x52, 0x45, 0x53, 0x5f, 0x45, 0x4e, 0x56, 0x4f, 0x59, 0x5f, 0x42, 0x4f, 0x4f, 0x54, 0x53, 0x54, 0x52, 0x41, 0x50, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, - 0x47, 0x55, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x03, 0x2a, 0xcc, 0x01, 0x0a, 0x0b, 0x53, + 0x47, 0x55, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x03, 0x2a, 0xea, 0x01, 0x0a, 0x0b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x1c, 0x0a, 0x18, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x53, 0x45, 0x52, 0x56, @@ -606,45 +611,47 @@ var file_proto_public_pbdataplane_dataplane_proto_rawDesc = []byte{ 0x44, 0x5f, 0x54, 0x45, 0x52, 0x4d, 0x49, 0x4e, 0x41, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x47, 0x41, 0x54, 0x45, 0x57, 0x41, 0x59, 0x10, 0x04, 0x12, 0x20, 0x0a, 0x1c, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x49, 0x4e, 0x47, 0x52, 0x45, 0x53, 0x53, 0x5f, - 0x47, 0x41, 0x54, 0x45, 0x57, 0x41, 0x59, 0x10, 0x05, 0x32, 0xde, 0x02, 0x0a, 0x10, 0x44, 0x61, - 0x74, 0x61, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xac, - 0x01, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x44, - 0x61, 0x74, 0x61, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, - 0x12, 0x40, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, + 0x47, 0x41, 0x54, 0x45, 0x57, 0x41, 0x59, 0x10, 0x05, 0x12, 0x1c, 0x0a, 0x18, 0x53, 0x45, 0x52, + 0x56, 0x49, 0x43, 0x45, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x41, 0x50, 0x49, 0x5f, 0x47, 0x41, + 0x54, 0x45, 0x57, 0x41, 0x59, 0x10, 0x06, 0x32, 0xde, 0x02, 0x0a, 0x10, 0x44, 0x61, 0x74, 0x61, + 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xac, 0x01, 0x0a, + 0x1d, 0x47, 0x65, 0x74, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, + 0x61, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x40, + 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, + 0x6c, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x53, + 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6c, 0x61, 0x6e, + 0x65, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x41, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6c, - 0x61, 0x6e, 0x65, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x41, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, - 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, - 0x47, 0x65, 0x74, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, - 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xe2, 0x86, 0x04, 0x02, 0x08, 0x02, 0x12, 0x9a, 0x01, - 0x0a, 0x17, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x76, 0x6f, 0x79, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, - 0x72, 0x61, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x3a, 0x2e, 0x68, 0x61, 0x73, 0x68, - 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x64, 0x61, 0x74, - 0x61, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x76, 0x6f, 0x79, 0x42, - 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3b, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, - 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6c, 0x61, - 0x6e, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x76, 0x6f, 0x79, 0x42, 0x6f, 0x6f, 0x74, 0x73, - 0x74, 0x72, 0x61, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x06, 0xe2, 0x86, 0x04, 0x02, 0x08, 0x02, 0x42, 0xf0, 0x01, 0x0a, 0x1e, 0x63, - 0x6f, 0x6d, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, - 0x73, 0x75, 0x6c, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x42, 0x0e, 0x44, - 0x61, 0x74, 0x61, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, - 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, - 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2f, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2d, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2f, 0x70, 0x62, 0x64, 0x61, 0x74, 0x61, - 0x70, 0x6c, 0x61, 0x6e, 0x65, 0xa2, 0x02, 0x03, 0x48, 0x43, 0x44, 0xaa, 0x02, 0x1a, 0x48, 0x61, - 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x44, - 0x61, 0x74, 0x61, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0xca, 0x02, 0x1a, 0x48, 0x61, 0x73, 0x68, 0x69, - 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x44, 0x61, 0x74, 0x61, - 0x70, 0x6c, 0x61, 0x6e, 0x65, 0xe2, 0x02, 0x26, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, - 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6c, 0x61, - 0x6e, 0x65, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, - 0x1c, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x73, - 0x75, 0x6c, 0x3a, 0x3a, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x6e, 0x65, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x06, 0xe2, 0x86, 0x04, 0x02, 0x08, 0x02, 0x12, 0x9a, 0x01, 0x0a, 0x17, + 0x47, 0x65, 0x74, 0x45, 0x6e, 0x76, 0x6f, 0x79, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, + 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x3a, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, + 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x70, + 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x76, 0x6f, 0x79, 0x42, 0x6f, 0x6f, + 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x3b, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, + 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6c, 0x61, 0x6e, 0x65, + 0x2e, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x76, 0x6f, 0x79, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, + 0x61, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x06, 0xe2, 0x86, 0x04, 0x02, 0x08, 0x02, 0x42, 0xf0, 0x01, 0x0a, 0x1e, 0x63, 0x6f, 0x6d, + 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, + 0x6c, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x42, 0x0e, 0x44, 0x61, 0x74, + 0x61, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x34, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, + 0x6f, 0x72, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2d, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2f, 0x70, 0x62, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6c, + 0x61, 0x6e, 0x65, 0xa2, 0x02, 0x03, 0x48, 0x43, 0x44, 0xaa, 0x02, 0x1a, 0x48, 0x61, 0x73, 0x68, + 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x44, 0x61, 0x74, + 0x61, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0xca, 0x02, 0x1a, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, + 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6c, + 0x61, 0x6e, 0x65, 0xe2, 0x02, 0x26, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, + 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6c, 0x61, 0x6e, 0x65, + 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x1c, 0x48, + 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, + 0x3a, 0x3a, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( diff --git a/proto-public/pbdataplane/dataplane.proto b/proto-public/pbdataplane/dataplane.proto index d10957161..1eb6b3f37 100644 --- a/proto-public/pbdataplane/dataplane.proto +++ b/proto-public/pbdataplane/dataplane.proto @@ -65,6 +65,10 @@ enum ServiceKind { // ServiceKind Ingress Gateway is an Ingress Gateway for the Connect feature. // This service will ingress connections into the service mesh. SERVICE_KIND_INGRESS_GATEWAY = 5; + + // ServiceKind API Gateway is an API Gateway for the Connect feature. + // This service will ingress connections in to the service mesh. + SERVICE_KIND_API_GATEWAY = 6; } message GetEnvoyBootstrapParamsResponse {