mirror of https://github.com/status-im/consul.git
Update trust bundle into proxy-state-template (#18550)
This commit is contained in:
parent
17667a1c75
commit
4f9955d91e
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -75,6 +72,8 @@ 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"
|
||||
|
@ -82,6 +81,7 @@ import (
|
|||
"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"
|
||||
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
}
|
|
@ -16,22 +16,26 @@ 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).
|
||||
WithWatch(catalog.ServiceEndpointsType, mapper.MapLink).
|
||||
WithPlacement(controller.PlacementEachServer).
|
||||
WithReconciler(&xdsReconciler{bimapper: mapper, updater: updater})
|
||||
WithReconciler(&xdsReconciler{bimapper: mapper, updater: updater, fetchTrustBundle: fetcher})
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ type xdsControllerTestSuite struct {
|
|||
ctl *xdsReconciler
|
||||
mapper *bimapper.Mapper
|
||||
updater *mockUpdater
|
||||
fetcher TrustBundleFetcher
|
||||
|
||||
fooProxyStateTemplate *pbresource.Resource
|
||||
barProxyStateTemplate *pbresource.Resource
|
||||
|
@ -48,6 +49,7 @@ type xdsControllerTestSuite struct {
|
|||
fooBarService *pbresource.Resource
|
||||
expectedFooProxyStateEndpoints map[string]*pbproxystate.Endpoints
|
||||
expectedBarProxyStateEndpoints map[string]*pbproxystate.Endpoints
|
||||
expectedTrustBundle map[string]*pbproxystate.TrustBundle
|
||||
}
|
||||
|
||||
func (suite *xdsControllerTestSuite) SetupTest() {
|
||||
|
@ -55,16 +57,27 @@ func (suite *xdsControllerTestSuite) SetupTest() {
|
|||
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.
|
||||
|
@ -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
|
||||
|
@ -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, suite.fetcher))
|
||||
mgr.SetRaftLeader(true)
|
||||
go mgr.Run(suite.ctx)
|
||||
|
||||
|
@ -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:
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -16,6 +16,7 @@ const (
|
|||
StatusReasonEndpointNotRead = "ProxyStateEndpointReferenceReadError"
|
||||
StatusReasonCreatingProxyStateEndpointsFailed = "ProxyStateEndpointsNotComputed"
|
||||
StatusReasonPushChangeFailed = "ProxyStatePushChangeFailed"
|
||||
StatusReasonTrustBundleFetchFailed = "ProxyStateTrustBundleFetchFailed"
|
||||
)
|
||||
|
||||
func KeyFromID(id *pbresource.ID) string {
|
||||
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue