mirror of https://github.com/hashicorp/consul
Browse Source
* NET-6426 Create ProxyStateTemplate when reconciling MeshGateway resource * Add TODO for switching fetch method based on gateway type * Use gateway-kind in workload metadata instead of owner reference * Create ProxyStateTemplate builder for gatewayproxy controller * Update to use new controller interface * Add copyright headers * Set correct name for ProxyStateTemplate identity reference * Generate empty ProxyStateTemplate by fetching MeshGateway This cheats and looks up the MeshGateway directly. In the future, we will need a Workload => xGateway mapper * Specify owner reference when writing ProxyStateTemplate * Update dependency mapper to account for multiple controllers per resource type * Regenerate v2 resource dependencies map * Add helpful trace logs, tag TODOs with ticket identifierspull/20191/head^2
Nathan Coleman
11 months ago
committed by
GitHub
7 changed files with 283 additions and 3 deletions
@ -0,0 +1,65 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package builder |
||||
|
||||
import ( |
||||
"github.com/hashicorp/consul/internal/mesh/internal/types" |
||||
pbauth "github.com/hashicorp/consul/proto-public/pbauth/v2beta1" |
||||
meshv2beta1 "github.com/hashicorp/consul/proto-public/pbmesh/v2beta1" |
||||
"github.com/hashicorp/consul/proto-public/pbmesh/v2beta1/pbproxystate" |
||||
"github.com/hashicorp/consul/proto-public/pbresource" |
||||
) |
||||
|
||||
type proxyStateTemplateBuilder struct { |
||||
workload *types.DecodedWorkload |
||||
} |
||||
|
||||
func NewProxyStateTemplateBuilder(workload *types.DecodedWorkload) *proxyStateTemplateBuilder { |
||||
return &proxyStateTemplateBuilder{ |
||||
workload: workload, |
||||
} |
||||
} |
||||
|
||||
func (b *proxyStateTemplateBuilder) identity() *pbresource.Reference { |
||||
return &pbresource.Reference{ |
||||
Name: b.workload.Data.Identity, |
||||
Tenancy: b.workload.Id.Tenancy, |
||||
Type: pbauth.WorkloadIdentityType, |
||||
} |
||||
} |
||||
|
||||
func (b *proxyStateTemplateBuilder) listeners() []*pbproxystate.Listener { |
||||
// TODO NET-6429
|
||||
return nil |
||||
} |
||||
|
||||
func (b *proxyStateTemplateBuilder) clusters() map[string]*pbproxystate.Cluster { |
||||
// TODO NET-6430
|
||||
return nil |
||||
} |
||||
|
||||
func (b *proxyStateTemplateBuilder) endpoints() map[string]*pbproxystate.Endpoints { |
||||
// TODO NET-6431
|
||||
return nil |
||||
} |
||||
|
||||
func (b *proxyStateTemplateBuilder) routes() map[string]*pbproxystate.Route { |
||||
// TODO NET-6428
|
||||
return nil |
||||
} |
||||
|
||||
func (b *proxyStateTemplateBuilder) Build() *meshv2beta1.ProxyStateTemplate { |
||||
return &meshv2beta1.ProxyStateTemplate{ |
||||
ProxyState: &meshv2beta1.ProxyState{ |
||||
Identity: b.identity(), |
||||
Listeners: b.listeners(), |
||||
Clusters: b.clusters(), |
||||
Endpoints: b.endpoints(), |
||||
Routes: b.routes(), |
||||
}, |
||||
RequiredEndpoints: make(map[string]*pbproxystate.EndpointRef), |
||||
RequiredLeafCertificates: make(map[string]*pbproxystate.LeafCertificateRef), |
||||
RequiredTrustBundles: make(map[string]*pbproxystate.TrustBundleRef), |
||||
} |
||||
} |
@ -0,0 +1,137 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package gatewayproxy |
||||
|
||||
import ( |
||||
"context" |
||||
|
||||
"google.golang.org/protobuf/proto" |
||||
"google.golang.org/protobuf/types/known/anypb" |
||||
|
||||
"github.com/hashicorp/consul/internal/controller" |
||||
"github.com/hashicorp/consul/internal/controller/dependency" |
||||
"github.com/hashicorp/consul/internal/mesh/internal/controllers/gatewayproxy/builder" |
||||
"github.com/hashicorp/consul/internal/mesh/internal/controllers/gatewayproxy/fetcher" |
||||
"github.com/hashicorp/consul/internal/mesh/internal/controllers/sidecarproxy/cache" |
||||
"github.com/hashicorp/consul/internal/resource" |
||||
pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v2beta1" |
||||
pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v2beta1" |
||||
"github.com/hashicorp/consul/proto-public/pbresource" |
||||
) |
||||
|
||||
// ControllerName is the name for this controller. It's used for logging or status keys.
|
||||
const ControllerName = "consul.io/gateway-proxy-controller" |
||||
|
||||
// Controller is responsible for triggering reconciler for watched resources
|
||||
func Controller(cache *cache.Cache) *controller.Controller { |
||||
// TODO NET-7016 Use caching functionality in NewController being implemented at time of writing
|
||||
// TODO NET-7017 Add the host of other types we should watch
|
||||
return controller.NewController(ControllerName, pbmesh.ProxyStateTemplateType). |
||||
WithWatch(pbcatalog.WorkloadType, dependency.ReplaceType(pbmesh.ProxyStateTemplateType)). |
||||
WithWatch(pbmesh.ComputedProxyConfigurationType, dependency.ReplaceType(pbmesh.ProxyStateTemplateType)). |
||||
WithReconciler(&reconciler{ |
||||
cache: cache, |
||||
}) |
||||
} |
||||
|
||||
// reconciler is responsible for managing the ProxyStateTemplate for all
|
||||
// gateway types: mesh, api (future) and terminating (future).
|
||||
type reconciler struct { |
||||
cache *cache.Cache |
||||
} |
||||
|
||||
// Reconcile is responsible for creating and updating the pbmesh.ProxyStateTemplate
|
||||
// for all gateway types. Since the ProxyStateTemplates managed here will always have
|
||||
// an owner reference pointing to the corresponding pbmesh.MeshGateway, deletion is
|
||||
// left to the garbage collector.
|
||||
func (r *reconciler) Reconcile(ctx context.Context, rt controller.Runtime, req controller.Request) error { |
||||
rt.Logger = rt.Logger.With("resource-id", req.ID) |
||||
rt.Logger.Trace("reconciling proxy state template") |
||||
|
||||
// Instantiate a data fetcher to fetch all reconciliation data.
|
||||
dataFetcher := fetcher.New(rt.Client, r.cache) |
||||
|
||||
workloadID := resource.ReplaceType(pbcatalog.WorkloadType, req.ID) |
||||
workload, err := dataFetcher.FetchWorkload(ctx, workloadID) |
||||
if err != nil { |
||||
rt.Logger.Error("error reading the associated workload", "error", err) |
||||
return err |
||||
} |
||||
|
||||
if workload == nil { |
||||
// If workload has been deleted, then return as ProxyStateTemplate should be cleaned up
|
||||
// by the garbage collector because of the owner reference.
|
||||
rt.Logger.Trace("workload doesn't exist; skipping reconciliation", "workload", workloadID) |
||||
return nil |
||||
} |
||||
|
||||
// If the workload is not for a xGateway, let the sidecarproxy reconciler handle it
|
||||
if gatewayKind := workload.Metadata["gateway-kind"]; gatewayKind == "" { |
||||
rt.Logger.Trace("workload is not a gateway; skipping reconciliation", "workload", workloadID) |
||||
return nil |
||||
} |
||||
|
||||
// TODO NET-7014 Determine what gateway controls this workload
|
||||
// For now, we cheat by knowing the MeshGateway's name, type + tenancy ahead of time
|
||||
gatewayID := &pbresource.ID{ |
||||
Name: "mesh-gateway", |
||||
Type: pbmesh.MeshGatewayType, |
||||
Tenancy: resource.DefaultPartitionedTenancy(), |
||||
} |
||||
|
||||
// Check if the gateway exists.
|
||||
gateway, err := dataFetcher.FetchMeshGateway(ctx, gatewayID) |
||||
if err != nil { |
||||
rt.Logger.Error("error reading the associated gateway", "error", err) |
||||
return err |
||||
} |
||||
if gateway == nil { |
||||
// If gateway has been deleted, then return as ProxyStateTemplate should be
|
||||
// cleaned up by the garbage collector because of the owner reference.
|
||||
rt.Logger.Trace("gateway doesn't exist; skipping reconciliation", "gateway", gatewayID) |
||||
return nil |
||||
} |
||||
|
||||
proxyStateTemplate, err := dataFetcher.FetchProxyStateTemplate(ctx, req.ID) |
||||
if err != nil { |
||||
rt.Logger.Error("error reading proxy state template", "error", err) |
||||
return nil |
||||
} |
||||
|
||||
if proxyStateTemplate == nil { |
||||
req.ID.Uid = "" |
||||
rt.Logger.Trace("proxy state template for this gateway doesn't yet exist; generating a new one") |
||||
} |
||||
|
||||
newPST := builder.NewProxyStateTemplateBuilder(workload).Build() |
||||
|
||||
proxyTemplateData, err := anypb.New(newPST) |
||||
if err != nil { |
||||
rt.Logger.Error("error creating proxy state template data", "error", err) |
||||
return err |
||||
} |
||||
rt.Logger.Trace("updating proxy state template") |
||||
|
||||
// If we're not creating a new PST and the generated one matches the existing one, nothing to do
|
||||
if proxyStateTemplate != nil && proto.Equal(proxyStateTemplate.Data, newPST) { |
||||
rt.Logger.Trace("no changes to existing proxy state template") |
||||
return nil |
||||
} |
||||
|
||||
// Write the created/updated ProxyStateTemplate with MeshGateway owner
|
||||
_, err = rt.Client.Write(ctx, &pbresource.WriteRequest{ |
||||
Resource: &pbresource.Resource{ |
||||
Id: req.ID, |
||||
Metadata: map[string]string{"gateway-kind": workload.Metadata["gateway-kind"]}, |
||||
Owner: workload.Resource.Id, |
||||
Data: proxyTemplateData, |
||||
}, |
||||
}) |
||||
if err != nil { |
||||
rt.Logger.Error("error writing proxy state template", "error", err) |
||||
return err |
||||
} |
||||
|
||||
return nil |
||||
} |
@ -0,0 +1,60 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package fetcher |
||||
|
||||
import ( |
||||
"context" |
||||
|
||||
"github.com/hashicorp/consul/internal/mesh/internal/controllers/sidecarproxy/cache" |
||||
"github.com/hashicorp/consul/internal/mesh/internal/types" |
||||
"github.com/hashicorp/consul/internal/resource" |
||||
pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v2beta1" |
||||
pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v2beta1" |
||||
"github.com/hashicorp/consul/proto-public/pbresource" |
||||
) |
||||
|
||||
type Fetcher struct { |
||||
client pbresource.ResourceServiceClient |
||||
cache *cache.Cache |
||||
} |
||||
|
||||
func New(client pbresource.ResourceServiceClient, cache *cache.Cache) *Fetcher { |
||||
return &Fetcher{ |
||||
client: client, |
||||
cache: cache, |
||||
} |
||||
} |
||||
|
||||
func (f *Fetcher) FetchMeshGateway(ctx context.Context, id *pbresource.ID) (*types.DecodedMeshGateway, error) { |
||||
dec, err := resource.GetDecodedResource[*pbmesh.MeshGateway](ctx, f.client, id) |
||||
if err != nil { |
||||
return nil, err |
||||
} else if dec == nil { |
||||
return nil, nil |
||||
} |
||||
|
||||
return dec, err |
||||
} |
||||
|
||||
func (f *Fetcher) FetchProxyStateTemplate(ctx context.Context, id *pbresource.ID) (*types.DecodedProxyStateTemplate, error) { |
||||
dec, err := resource.GetDecodedResource[*pbmesh.ProxyStateTemplate](ctx, f.client, id) |
||||
if err != nil { |
||||
return nil, err |
||||
} else if dec == nil { |
||||
return nil, nil |
||||
} |
||||
|
||||
return dec, err |
||||
} |
||||
|
||||
func (f *Fetcher) FetchWorkload(ctx context.Context, id *pbresource.ID) (*types.DecodedWorkload, error) { |
||||
dec, err := resource.GetDecodedResource[*pbcatalog.Workload](ctx, f.client, id) |
||||
if err != nil { |
||||
return nil, err |
||||
} else if dec == nil { |
||||
return nil, nil |
||||
} |
||||
|
||||
return dec, err |
||||
} |
Loading…
Reference in new issue