Skip to content

Commit

Permalink
Update trust bundle into proxy-state-template
Browse files Browse the repository at this point in the history
  • Loading branch information
thisisnotashwin committed Aug 22, 2023
1 parent 0d60380 commit 026e2bd
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 13 deletions.
22 changes: 18 additions & 4 deletions agent/consul/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,8 @@ import (
"sync/atomic"
"time"

"github.com/hashicorp/consul/internal/resource"

"github.com/armon/go-metrics"
"github.com/hashicorp/consul-net-rpc/net/rpc"
"github.com/hashicorp/go-connlimit"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-memdb"
Expand All @@ -38,8 +37,6 @@ import (
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/reflection"

"github.com/hashicorp/consul-net-rpc/net/rpc"

"github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/acl/resolver"
"github.com/hashicorp/consul/agent/blockingquery"
Expand Down Expand Up @@ -75,13 +72,16 @@ import (
"github.com/hashicorp/consul/agent/token"
"github.com/hashicorp/consul/internal/catalog"
"github.com/hashicorp/consul/internal/controller"
"github.com/hashicorp/consul/internal/mesh"
"github.com/hashicorp/consul/internal/resource"
"github.com/hashicorp/consul/internal/resource/demo"
"github.com/hashicorp/consul/internal/resource/reaper"
raftstorage "github.com/hashicorp/consul/internal/storage/raft"
"github.com/hashicorp/consul/lib"
"github.com/hashicorp/consul/lib/routine"
"github.com/hashicorp/consul/lib/stringslice"
"github.com/hashicorp/consul/logging"
"github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1/pbproxystate"
"github.com/hashicorp/consul/proto-public/pbresource"
"github.com/hashicorp/consul/proto/private/pbsubscribe"
"github.com/hashicorp/consul/tlsutil"
Expand Down Expand Up @@ -876,6 +876,20 @@ func NewServer(config *Config, flat Deps, externalGRPCServer *grpc.Server, incom
func (s *Server) registerControllers(deps Deps) {
if stringslice.Contains(deps.Experiments, catalogResourceExperimentName) {
catalog.RegisterControllers(s.controllerManager, catalog.DefaultControllerDependencies())
mesh.RegisterControllers(s.controllerManager, mesh.ControllerDependencies{
TrustBundleFetcher: func() (*pbproxystate.TrustBundle, error) {
var bundle pbproxystate.TrustBundle
roots, err := s.getCARoots(nil, s.GetState())
if err != nil {
return nil, err
}
bundle.TrustDomain = roots.TrustDomain
for _, root := range roots.Roots {
bundle.Roots = append(bundle.Roots, root.RootCert)
}
return &bundle, nil
},
})
}

reaper.RegisterControllers(s.controllerManager)
Expand Down
10 changes: 10 additions & 0 deletions internal/mesh/exports.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
package mesh

import (
"github.com/hashicorp/consul/internal/controller"
"github.com/hashicorp/consul/internal/mesh/internal/controllers"
"github.com/hashicorp/consul/internal/mesh/internal/types"
"github.com/hashicorp/consul/internal/resource"
)
Expand Down Expand Up @@ -57,3 +59,11 @@ var (
func RegisterTypes(r resource.Registry) {
types.Register(r)
}

// RegisterControllers registers controllers for the mesh types with
// the given controller Manager.
func RegisterControllers(mgr *controller.Manager, deps ControllerDependencies) {
controllers.Register(mgr, deps)
}

type ControllerDependencies = controllers.Dependencies
22 changes: 22 additions & 0 deletions internal/mesh/internal/controllers/register.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package controllers

import (
"github.com/hashicorp/consul/internal/catalog"
"github.com/hashicorp/consul/internal/controller"
"github.com/hashicorp/consul/internal/mesh/internal/controllers/xds"
"github.com/hashicorp/consul/internal/mesh/internal/types"
"github.com/hashicorp/consul/internal/resource/mappers/bimapper"
)

type Dependencies struct {
TrustBundleFetcher xds.TrustBundleFetcher
}

func Register(mgr *controller.Manager, deps Dependencies) {
mapper := bimapper.New(types.ProxyStateTemplateType, catalog.ServiceEndpointsType)
// TODO: Pass in a "real" updater once proxy tracker work has completed.
mgr.Register(xds.Controller(mapper, nil, deps.TrustBundleFetcher))
}
31 changes: 26 additions & 5 deletions internal/mesh/internal/controllers/xds/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ import (

const ControllerName = "consul.io/xds-controller"

func Controller(mapper *bimapper.Mapper, updater ProxyUpdater) controller.Controller {
if mapper == nil || updater == nil {
panic("mapper and updater are required")
func Controller(mapper *bimapper.Mapper, updater ProxyUpdater, fetcher TrustBundleFetcher) controller.Controller {
//if mapper == nil || updater == nil || fetcher == nil {
if mapper == nil || fetcher == nil {
panic("mapper, updater and fetcher are required")
}

return controller.ForType(types.ProxyStateTemplateType).
Expand All @@ -28,10 +29,13 @@ func Controller(mapper *bimapper.Mapper, updater ProxyUpdater) controller.Contro
}

type xdsReconciler struct {
bimapper *bimapper.Mapper
updater ProxyUpdater
bimapper *bimapper.Mapper
updater ProxyUpdater
fetchTrustBundle TrustBundleFetcher
}

type TrustBundleFetcher func() (*pbproxystate.TrustBundle, error)

// ProxyUpdater is an interface that defines the ability to push proxy updates to the updater
// and also check its connectivity to the server.
type ProxyUpdater interface {
Expand Down Expand Up @@ -78,6 +82,23 @@ func (r *xdsReconciler) Reconcile(ctx context.Context, rt controller.Runtime, re

return err
}

// TODO: Fetch trust bundles for all peers when peering is supported.
trustBundle, err := r.fetchTrustBundle()
if err != nil {
rt.Logger.Error("error fetching root trust bundle", "error", err)
// Set the status.
statusCondition = status.ConditionRejectedTrustBundleFetchFailed(status.KeyFromID(req.ID))
status.WriteStatusIfChanged(ctx, rt, pstResource, statusCondition)
return err
}

if proxyStateTemplate.Template.ProxyState.TrustBundles == nil {
proxyStateTemplate.Template.ProxyState.TrustBundles = make(map[string]*pbproxystate.TrustBundle)
}
// TODO: Figure out the correct key for the default trust bundle.
proxyStateTemplate.Template.ProxyState.TrustBundles["local"] = trustBundle

if proxyStateTemplate.Template.ProxyState.Endpoints == nil {
proxyStateTemplate.Template.ProxyState.Endpoints = make(map[string]*pbproxystate.Endpoints)
}
Expand Down
48 changes: 44 additions & 4 deletions internal/mesh/internal/controllers/xds/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,23 +48,36 @@ type xdsControllerTestSuite struct {
fooBarService *pbresource.Resource
expectedFooProxyStateEndpoints map[string]*pbproxystate.Endpoints
expectedBarProxyStateEndpoints map[string]*pbproxystate.Endpoints
fetcher TrustBundleFetcher
expectedTrustBundle map[string]*pbproxystate.TrustBundle
}

func (suite *xdsControllerTestSuite) SetupTest() {
suite.ctx = testutil.TestContext(suite.T())
resourceClient := svctest.RunResourceService(suite.T(), types.Register, catalog.RegisterTypes)
suite.runtime = controller.Runtime{Client: resourceClient, Logger: testutil.Logger(suite.T())}
suite.client = resourcetest.NewClient(resourceClient)
suite.fetcher = mockFetcher

suite.mapper = bimapper.New(types.ProxyStateTemplateType, catalog.ServiceEndpointsType)
suite.updater = NewMockUpdater()

suite.ctl = &xdsReconciler{
bimapper: suite.mapper,
updater: suite.updater,
bimapper: suite.mapper,
updater: suite.updater,
fetchTrustBundle: suite.fetcher,
}
}

func mockFetcher() (*pbproxystate.TrustBundle, error) {
var bundle pbproxystate.TrustBundle
bundle = pbproxystate.TrustBundle{
TrustDomain: "some-trust-domain",
Roots: []string{"some-root", "some-other-root"},
}
return &bundle, nil
}

// This test ensures when a ProxyState is deleted, it is no longer tracked in the mapper.
func (suite *xdsControllerTestSuite) TestReconcile_NoProxyStateTemplate() {
// Track the id of a non-existent ProxyStateTemplate.
Expand Down Expand Up @@ -217,6 +230,25 @@ func (suite *xdsControllerTestSuite) TestReconcile_ProxyStateTemplateComputesEnd
prototest.AssertDeepEqual(suite.T(), suite.expectedFooProxyStateEndpoints, actualEndpoints)
}

func (suite *xdsControllerTestSuite) TestReconcile_ProxyStateTemplateSetsTrustBundles() {
// This test is a happy path creation test to make sure pbproxystate.Template.TrustBundles are created in the computed
// pbmesh.ProxyState from the TrustBundleFetcher.
suite.setupFooProxyStateTemplateAndEndpoints()

// Run the reconcile.
err := suite.ctl.Reconcile(context.Background(), suite.runtime, controller.Request{
ID: suite.fooProxyStateTemplate.Id,
})
require.NoError(suite.T(), err)

// Assert on the status.
suite.client.RequireStatusCondition(suite.T(), suite.fooProxyStateTemplate.Id, ControllerName, status.ConditionAccepted())

// Assert that the endpoints computed in the controller matches the expected endpoints.
actualTrustBundle := suite.updater.GetTrustBundle(suite.fooProxyStateTemplate.Id.Name)
prototest.AssertDeepEqual(suite.T(), suite.expectedTrustBundle, actualTrustBundle)
}

// This test is a happy path creation test that calls reconcile multiple times with a more complex setup. This
// scenario is trickier to test in the controller test because the end computation of the xds controller is not
// stored in the state store. So this test ensures that between multiple reconciles the correct ProxyStates are
Expand Down Expand Up @@ -257,7 +289,7 @@ func (suite *xdsControllerTestSuite) TestReconcile_MultipleProxyStateTemplatesCo
func (suite *xdsControllerTestSuite) TestController_ComputeAddUpdateEndpoints() {
// Run the controller manager.
mgr := controller.NewManager(suite.client, suite.runtime.Logger)
mgr.Register(Controller(suite.mapper, suite.updater))
mgr.Register(Controller(suite.mapper, suite.updater, mockFetcher))
mgr.SetRaftLeader(true)
go mgr.Run(suite.ctx)

Expand Down Expand Up @@ -470,12 +502,20 @@ func (suite *xdsControllerTestSuite) setupFooProxyStateTemplateAndEndpoints() {
},
}},
}

expectedTrustBundle := map[string]*pbproxystate.TrustBundle{
"local": {
TrustDomain: "some-trust-domain",
Roots: []string{"some-root", "some-other-root"},
},
}

suite.fooService = fooService
suite.fooEndpoints = fooEndpoints
suite.fooEndpointRefs = fooRequiredEndpoints
suite.fooProxyStateTemplate = fooProxyStateTemplate
suite.expectedFooProxyStateEndpoints = expectedFooProxyStateEndpoints

suite.expectedTrustBundle = expectedTrustBundle
}

// Setup:
Expand Down
10 changes: 10 additions & 0 deletions internal/mesh/internal/controllers/xds/mock_updater.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,16 @@ func (p *mockUpdater) GetEndpoints(name string) map[string]*pbproxystate.Endpoin
return nil
}

func (p *mockUpdater) GetTrustBundle(name string) map[string]*pbproxystate.TrustBundle {
p.lock.Lock()
defer p.lock.Unlock()
ps, ok := p.latestPs[name]
if ok {
return ps.TrustBundles
}
return nil
}

func (p *mockUpdater) Set(name string, ps *pbmesh.ProxyState) {
p.lock.Lock()
defer p.lock.Unlock()
Expand Down
9 changes: 9 additions & 0 deletions internal/mesh/internal/controllers/xds/status/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const (
StatusReasonEndpointNotRead = "ProxyStateEndpointReferenceReadError"
StatusReasonCreatingProxyStateEndpointsFailed = "ProxyStateEndpointsNotComputed"
StatusReasonPushChangeFailed = "ProxyStatePushChangeFailed"
StatusReasonTrustBundleFetchFailed = "ProxyStateTrustBundleFetchFailed"
)

func KeyFromID(id *pbresource.ID) string {
Expand Down Expand Up @@ -65,6 +66,14 @@ func ConditionRejectedPushChangeFailed(pstRef string) *pbresource.Condition {
Message: fmt.Sprintf("failed to push change for proxy state template %q", pstRef),
}
}
func ConditionRejectedTrustBundleFetchFailed(pstRef string) *pbresource.Condition {
return &pbresource.Condition{
Type: StatusConditionProxyStateAccepted,
State: pbresource.Condition_STATE_FALSE,
Reason: StatusReasonTrustBundleFetchFailed,
Message: fmt.Sprintf("failed to fetch trust bundle for proxy state template %q", pstRef),
}
}

// WriteStatusIfChanged updates the ProxyStateTemplate status if it has changed.
func WriteStatusIfChanged(ctx context.Context, rt controller.Runtime, res *pbresource.Resource, condition *pbresource.Condition) {
Expand Down

0 comments on commit 026e2bd

Please sign in to comment.