[NET-6438] Add tenancy to xDS Tests (#19551)

* [NET-6438] Add tenancy to xDS Tests

* [NET-6438] Add tenancy to xDS Tests
- Fixing imports

* [NET-6438] Add tenancy to xDS Tests
- Added cleanup post test run

* [NET-6356] Add tenancy to xDS Tests
- Added cleanup post test run

* [NET-6438] Add tenancy to xDS Tests
- using t.Cleanup instead of defer delete

* [NET-6438] Add tenancy to xDS Tests
- rebased

* [NET-6438] Add tenancy to xDS Tests
- rebased
This commit is contained in:
Kumar Kavish 2023-11-10 15:32:36 +05:30 committed by GitHub
parent 005e1b9926
commit 68e7f27fd2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 696 additions and 566 deletions

View File

@ -5,7 +5,6 @@ package testing
import (
"context"
rtest "github.com/hashicorp/consul/internal/resource/resourcetest"
"testing"
"github.com/hashicorp/go-uuid"
@ -21,6 +20,7 @@ import (
internal "github.com/hashicorp/consul/agent/grpc-internal"
"github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/internal/resource"
rtest "github.com/hashicorp/consul/internal/resource/resourcetest"
"github.com/hashicorp/consul/internal/storage/inmem"
"github.com/hashicorp/consul/internal/tenancy"
"github.com/hashicorp/consul/proto-public/pbresource"

View File

@ -11,7 +11,6 @@ import (
"strings"
"testing"
"github.com/hashicorp/consul/internal/testing/golden"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"google.golang.org/protobuf/encoding/protojson"
@ -26,6 +25,7 @@ import (
"github.com/hashicorp/consul/internal/resource"
"github.com/hashicorp/consul/internal/resource/mappers/bimapper"
"github.com/hashicorp/consul/internal/resource/resourcetest"
"github.com/hashicorp/consul/internal/testing/golden"
pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v2beta1"
pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v2beta1"
"github.com/hashicorp/consul/proto-public/pbmesh/v2beta1/pbproxystate"
@ -51,6 +51,7 @@ type xdsControllerTestSuite struct {
leafCancels *LeafCancels
leafCertEvents chan controller.Event
signer *leafcert.TestSigner
tenancies []*pbresource.Tenancy
fooProxyStateTemplate *pbresource.Resource
barProxyStateTemplate *pbresource.Resource
@ -69,7 +70,7 @@ type xdsControllerTestSuite struct {
func (suite *xdsControllerTestSuite) SetupTest() {
suite.ctx = testutil.TestContext(suite.T())
resourceClient := svctest.RunResourceService(suite.T(), types.Register, catalog.RegisterTypes)
resourceClient := svctest.RunResourceServiceWithTenancies(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
@ -99,6 +100,8 @@ func (suite *xdsControllerTestSuite) SetupTest() {
leafCertEvents: suite.leafCertEvents,
datacenter: "dc1",
}
suite.tenancies = resourcetest.TestTenancies()
}
func mockFetcher() (*pbproxystate.TrustBundle, error) {
@ -112,8 +115,12 @@ func mockFetcher() (*pbproxystate.TrustBundle, error) {
// This test ensures when a ProxyState is deleted, it is no longer tracked in the mappers.
func (suite *xdsControllerTestSuite) TestReconcile_NoProxyStateTemplate() {
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
// Track the id of a non-existent ProxyStateTemplate.
proxyStateTemplateId := resourcetest.Resource(pbmesh.ProxyStateTemplateType, "not-found").ID()
proxyStateTemplateId := resourcetest.Resource(pbmesh.ProxyStateTemplateType, "not-found").WithTenancy(tenancy).ID()
suite.T().Cleanup(suite.deleteResourceFunc(proxyStateTemplateId))
suite.mapper.TrackItem(proxyStateTemplateId, []resource.ReferenceOrID{})
suite.leafMapper.TrackItem(proxyStateTemplateId, []resource.ReferenceOrID{})
require.False(suite.T(), suite.mapper.IsEmpty())
@ -128,16 +135,21 @@ func (suite *xdsControllerTestSuite) TestReconcile_NoProxyStateTemplate() {
// Assert that nothing is tracked in the endpoints mapper.
require.True(suite.T(), suite.mapper.IsEmpty())
require.True(suite.T(), suite.leafMapper.IsEmpty())
})
}
// This test ensures if the controller was previously tracking a ProxyStateTemplate, and now that proxy has
// disconnected from this server, it's ignored and removed from the mapper.
func (suite *xdsControllerTestSuite) TestReconcile_RemoveTrackingProxiesNotConnectedToServer() {
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
// Store the initial ProxyStateTemplate and track it in the mapper.
proxyStateTemplate := resourcetest.Resource(pbmesh.ProxyStateTemplateType, "test").
WithData(suite.T(), &pbmesh.ProxyStateTemplate{}).
WithTenancy(tenancy).
Write(suite.T(), suite.client)
suite.T().Cleanup(suite.deleteResourceFunc(proxyStateTemplate.Id))
suite.mapper.TrackItem(proxyStateTemplate.Id, []resource.ReferenceOrID{})
// Simulate the proxy disconnecting from this server. The resource still exists, but this proxy might be connected
@ -152,16 +164,18 @@ func (suite *xdsControllerTestSuite) TestReconcile_RemoveTrackingProxiesNotConne
// Assert that nothing is tracked in the mapper.
require.True(suite.T(), suite.mapper.IsEmpty())
})
}
// This test sets up the updater to return an error when calling PushChange, and ensures the status is set
// correctly.
func (suite *xdsControllerTestSuite) TestReconcile_PushChangeError() {
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
// Have the mock simulate an error from the PushChange call.
suite.updater.pushChangeError = true
// Setup a happy path scenario.
suite.setupFooProxyStateTemplateWithReferences()
suite.setupFooProxyStateTemplateWithReferences(tenancy)
// Run the reconcile.
err := suite.ctl.Reconcile(context.Background(), suite.runtime, controller.Request{
@ -171,14 +185,16 @@ func (suite *xdsControllerTestSuite) TestReconcile_PushChangeError() {
// Assert on the status reflecting endpoint not found.
suite.client.RequireStatusCondition(suite.T(), suite.fooProxyStateTemplate.Id, ControllerName, status.ConditionRejectedPushChangeFailed(status.KeyFromID(suite.fooProxyStateTemplate.Id)))
})
}
// This test sets up a ProxyStateTemplate that references a ServiceEndpoints that doesn't exist, and ensures the
// status is correct.
func (suite *xdsControllerTestSuite) TestReconcile_MissingEndpoint() {
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
// Set fooProxyStateTemplate with a reference to fooEndpoints, without storing fooEndpoints so the controller should
// notice it's missing.
fooEndpointsId := resourcetest.Resource(pbcatalog.ServiceEndpointsType, "foo-service").WithTenancy(resource.DefaultNamespacedTenancy()).ID()
fooEndpointsId := resourcetest.Resource(pbcatalog.ServiceEndpointsType, "foo-service").WithTenancy(tenancy).ID()
fooRequiredEndpoints := make(map[string]*pbproxystate.EndpointRef)
fooRequiredEndpoints["test-cluster-1"] = &pbproxystate.EndpointRef{
Id: fooEndpointsId,
@ -190,8 +206,11 @@ func (suite *xdsControllerTestSuite) TestReconcile_MissingEndpoint() {
RequiredEndpoints: fooRequiredEndpoints,
ProxyState: &pbmesh.ProxyState{},
}).
WithTenancy(tenancy).
Write(suite.T(), suite.client)
suite.T().Cleanup(suite.deleteResourceFunc(fooProxyStateTemplate.Id))
retry.Run(suite.T(), func(r *retry.R) {
suite.client.RequireResourceExists(r, fooProxyStateTemplate.Id)
})
@ -204,18 +223,20 @@ func (suite *xdsControllerTestSuite) TestReconcile_MissingEndpoint() {
// Assert on the status reflecting endpoint not found.
suite.client.RequireStatusCondition(suite.T(), fooProxyStateTemplate.Id, ControllerName, status.ConditionRejectedErrorReadingEndpoints(status.KeyFromID(fooEndpointsId), "rpc error: code = NotFound desc = resource not found"))
})
}
// This test sets up a ProxyStateTemplate that references a ServiceEndpoints that can't be read correctly, and
// checks the status is correct.
func (suite *xdsControllerTestSuite) TestReconcile_ReadEndpointError() {
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
badID := &pbresource.ID{
Type: &pbresource.Type{
Group: "not",
Kind: "found",
GroupVersion: "vfake",
},
Tenancy: &pbresource.Tenancy{Namespace: "default", Partition: "default", PeerName: "local"},
Tenancy: tenancy,
}
fooRequiredEndpoints := make(map[string]*pbproxystate.EndpointRef)
fooRequiredEndpoints["test-cluster-1"] = &pbproxystate.EndpointRef{
@ -228,8 +249,11 @@ func (suite *xdsControllerTestSuite) TestReconcile_ReadEndpointError() {
RequiredEndpoints: fooRequiredEndpoints,
ProxyState: &pbmesh.ProxyState{},
}).
WithTenancy(tenancy).
Write(suite.T(), suite.client)
suite.T().Cleanup(suite.deleteResourceFunc(fooProxyStateTemplate.Id))
retry.Run(suite.T(), func(r *retry.R) {
suite.client.RequireResourceExists(r, fooProxyStateTemplate.Id)
})
@ -245,15 +269,17 @@ func (suite *xdsControllerTestSuite) TestReconcile_ReadEndpointError() {
status.KeyFromID(badID),
"rpc error: code = InvalidArgument desc = id.name invalid: a resource name must consist of lower case alphanumeric characters or '-', must start and end with an alphanumeric character and be less than 64 characters, got: \"\"",
))
})
}
// This test is a happy path creation test to make sure pbproxystate.Endpoints are created in the computed
// pbmesh.ProxyState from the RequiredEndpoints references. More specific translations between endpoint references
// and pbproxystate.Endpoints are unit tested in endpoint_builder.go.
func (suite *xdsControllerTestSuite) TestReconcile_ProxyStateTemplateComputesEndpoints() {
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
// Set up fooEndpoints and fooProxyStateTemplate with a reference to fooEndpoints and store them in the state store.
// This setup saves expected values in the suite so it can be asserted against later.
suite.setupFooProxyStateTemplateWithReferences()
suite.setupFooProxyStateTemplateWithReferences(tenancy)
// Run the reconcile.
err := suite.ctl.Reconcile(context.Background(), suite.runtime, controller.Request{
@ -267,12 +293,14 @@ func (suite *xdsControllerTestSuite) TestReconcile_ProxyStateTemplateComputesEnd
// Assert that the endpoints computed in the controller matches the expected endpoints.
actualEndpoints := suite.updater.GetEndpoints(suite.fooProxyStateTemplate.Id.Name)
prototest.AssertDeepEqual(suite.T(), suite.expectedFooProxyStateEndpoints, actualEndpoints)
})
}
func (suite *xdsControllerTestSuite) TestReconcile_ProxyStateTemplateComputesLeafCerts() {
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
// Set up fooEndpoints and fooProxyStateTemplate with a reference to fooEndpoints and store them in the state store.
// This setup saves expected values in the suite so it can be asserted against later.
suite.setupFooProxyStateTemplateWithReferences()
suite.setupFooProxyStateTemplateWithReferences(tenancy)
// Run the reconcile.
err := suite.ctl.Reconcile(context.Background(), suite.runtime, controller.Request{
@ -287,17 +315,19 @@ func (suite *xdsControllerTestSuite) TestReconcile_ProxyStateTemplateComputesLea
actualLeafs := suite.updater.GetLeafs(suite.fooProxyStateTemplate.Id.Name)
for k, l := range actualLeafs {
pem, _ := pem.Decode([]byte(l.Cert))
cert, err := x509.ParseCertificate(pem.Bytes)
pemDecode, _ := pem.Decode([]byte(l.Cert))
cert, err := x509.ParseCertificate(pemDecode.Bytes)
require.NoError(suite.T(), err)
require.Equal(suite.T(), cert.URIs[0].String(), suite.expectedFooProxyStateSpiffes[k])
}
})
}
// This test is a happy path creation test to make sure pbproxystate.Template.TrustBundles are created in the computed
// pbmesh.ProxyState from the TrustBundleFetcher.
func (suite *xdsControllerTestSuite) TestReconcile_ProxyStateTemplateSetsTrustBundles() {
suite.setupFooProxyStateTemplateWithReferences()
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
suite.setupFooProxyStateTemplateWithReferences(tenancy)
// Run the reconcile.
err := suite.ctl.Reconcile(context.Background(), suite.runtime, controller.Request{
@ -311,6 +341,7 @@ func (suite *xdsControllerTestSuite) TestReconcile_ProxyStateTemplateSetsTrustBu
// 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
@ -318,9 +349,10 @@ func (suite *xdsControllerTestSuite) TestReconcile_ProxyStateTemplateSetsTrustBu
// stored in the state store. So this test ensures that between multiple reconciles the correct ProxyStates are
// computed for each ProxyStateTemplate.
func (suite *xdsControllerTestSuite) TestReconcile_MultipleProxyStateTemplatesComputesMultipleEndpoints() {
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
// Set up fooProxyStateTemplate and barProxyStateTemplate and their associated resources and store them. Resources
// and expected results are stored in the suite to assert against.
suite.setupFooBarProxyStateTemplateAndEndpoints()
suite.setupFooBarProxyStateTemplateAndEndpoints(tenancy)
// Reconcile the fooProxyStateTemplate.
err := suite.ctl.Reconcile(context.Background(), suite.runtime, controller.Request{
@ -347,17 +379,19 @@ func (suite *xdsControllerTestSuite) TestReconcile_MultipleProxyStateTemplatesCo
// Assert that the endpoints computed in the controller matches the expected endpoints.
actualBarEndpoints := suite.updater.GetEndpoints(suite.barProxyStateTemplate.Id.Name)
prototest.AssertDeepEqual(suite.T(), suite.expectedBarProxyStateEndpoints, actualBarEndpoints)
})
}
// Sets up a full controller, and tests that reconciles are getting triggered for the events it should.
func (suite *xdsControllerTestSuite) TestController_ComputeAddUpdateEndpointReferences() {
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
// Run the controller manager.
mgr := controller.NewManager(suite.client, suite.runtime.Logger)
mgr.Register(Controller(suite.mapper, suite.updater, suite.fetcher, suite.leafCertManager, suite.leafMapper, suite.leafCancels, "dc1"))
mgr.SetRaftLeader(true)
go mgr.Run(suite.ctx)
suite.setupFooProxyStateTemplateWithReferences()
suite.setupFooProxyStateTemplateWithReferences(tenancy)
// Assert that the expected ProxyState matches the actual ProxyState that PushChange was called with. This needs to
// be in a retry block unlike the Reconcile tests because the controller triggers asynchronously.
@ -371,7 +405,7 @@ func (suite *xdsControllerTestSuite) TestController_ComputeAddUpdateEndpointRefe
// Now, update the endpoint to be unhealthy. This will ensure the controller is getting triggered on changes to this
// endpoint that it should be tracking, even when the ProxyStateTemplate does not change.
resourcetest.Resource(pbcatalog.ServiceEndpointsType, "foo-service").
svc := resourcetest.Resource(pbcatalog.ServiceEndpointsType, "foo-service").
WithData(suite.T(), &pbcatalog.ServiceEndpoints{Endpoints: []*pbcatalog.Endpoint{
{
Ports: map[string]*pbcatalog.WorkloadPort{
@ -394,8 +428,11 @@ func (suite *xdsControllerTestSuite) TestController_ComputeAddUpdateEndpointRefe
},
}}).
WithOwner(suite.fooService.Id).
WithTenancy(tenancy).
Write(suite.T(), suite.client)
suite.T().Cleanup(suite.deleteResourceFunc(svc.Id))
// Wait for the endpoint to be written.
retry.Run(suite.T(), func(r *retry.R) {
suite.client.RequireVersionChanged(suite.T(), suite.fooEndpoints.Id, suite.fooEndpoints.Version)
@ -417,8 +454,11 @@ func (suite *xdsControllerTestSuite) TestController_ComputeAddUpdateEndpointRefe
// now tracks the newly added endpoint.
secondService := resourcetest.Resource(pbcatalog.ServiceType, "second-service").
WithData(suite.T(), &pbcatalog.Service{}).
WithTenancy(tenancy).
Write(suite.T(), suite.client)
suite.T().Cleanup(suite.deleteResourceFunc(secondService.Id))
secondEndpoints := resourcetest.Resource(pbcatalog.ServiceEndpointsType, "second-service").
WithData(suite.T(), &pbcatalog.ServiceEndpoints{Endpoints: []*pbcatalog.Endpoint{
{
@ -441,8 +481,11 @@ func (suite *xdsControllerTestSuite) TestController_ComputeAddUpdateEndpointRefe
},
}}).
WithOwner(secondService.Id).
WithTenancy(tenancy).
Write(suite.T(), suite.client)
suite.T().Cleanup(suite.deleteResourceFunc(secondEndpoints.Id))
// Update the endpoint references on the fooProxyStateTemplate.
suite.fooEndpointRefs["test-cluster-2"] = &pbproxystate.EndpointRef{
Id: secondEndpoints.Id,
@ -456,8 +499,11 @@ func (suite *xdsControllerTestSuite) TestController_ComputeAddUpdateEndpointRefe
ProxyState: &pbmesh.ProxyState{},
RequiredLeafCertificates: suite.fooLeafRefs,
}).
WithTenancy(tenancy).
Write(suite.T(), suite.client)
suite.T().Cleanup(suite.deleteResourceFunc(fooProxyStateTemplate.Id))
retry.Run(suite.T(), func(r *retry.R) {
suite.client.RequireVersionChanged(r, fooProxyStateTemplate.Id, oldVersion)
})
@ -494,18 +540,20 @@ func (suite *xdsControllerTestSuite) TestController_ComputeAddUpdateEndpointRefe
prototest.AssertDeepEqual(r, suite.expectedFooProxyStateEndpoints, actualEndpoints)
})
})
}
// Sets up a full controller, and tests that reconciles are getting triggered for the leaf cert events it should.
// This test ensures when a CA is updated, the controller is triggered to update the leaf cert when it changes.
func (suite *xdsControllerTestSuite) TestController_ComputeAddUpdateDeleteLeafReferences() {
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
// Run the controller manager.
mgr := controller.NewManager(suite.client, suite.runtime.Logger)
mgr.Register(Controller(suite.mapper, suite.updater, suite.fetcher, suite.leafCertManager, suite.leafMapper, suite.leafCancels, "dc1"))
mgr.SetRaftLeader(true)
go mgr.Run(suite.ctx)
suite.setupFooProxyStateTemplateWithReferences()
suite.setupFooProxyStateTemplateWithReferences(tenancy)
leafCertRef := suite.fooLeafRefs["foo-workload-identity"]
fooLeafResRef := leafResourceRef(leafCertRef.Name, leafCertRef.Namespace, leafCertRef.Partition)
@ -524,8 +572,8 @@ func (suite *xdsControllerTestSuite) TestController_ComputeAddUpdateDeleteLeafRe
// Assert that the leafs computed in the controller matches the expected leafs.
require.Len(r, actualLeafs, 1)
for k, l := range actualLeafs {
pem, _ := pem.Decode([]byte(l.Cert))
cert, err := x509.ParseCertificate(pem.Bytes)
pemDecode, _ := pem.Decode([]byte(l.Cert))
cert, err := x509.ParseCertificate(pemDecode.Bytes)
oldLeaf = cert
require.NoError(r, err)
require.Equal(r, cert.URIs[0].String(), suite.expectedFooProxyStateSpiffes[k])
@ -541,8 +589,8 @@ func (suite *xdsControllerTestSuite) TestController_ComputeAddUpdateDeleteLeafRe
actualLeafs := suite.updater.GetLeafs(suite.fooProxyStateTemplate.Id.Name)
require.Len(r, actualLeafs, 1)
for k, l := range actualLeafs {
pem, _ := pem.Decode([]byte(l.Cert))
cert, err := x509.ParseCertificate(pem.Bytes)
pemDecode, _ := pem.Decode([]byte(l.Cert))
cert, err := x509.ParseCertificate(pemDecode.Bytes)
// Ensure the leaf was actually updated by checking that the leaf we just got is different from the old leaf.
require.NotEqual(r, oldLeaf.Raw, cert.Raw)
require.NoError(r, err)
@ -563,8 +611,11 @@ func (suite *xdsControllerTestSuite) TestController_ComputeAddUpdateDeleteLeafRe
ProxyState: &pbmesh.ProxyState{},
RequiredLeafCertificates: suite.fooLeafRefs,
}).
WithTenancy(tenancy).
Write(suite.T(), suite.client)
suite.T().Cleanup(suite.deleteResourceFunc(fooProxyStateTemplate.Id))
retry.Run(suite.T(), func(r *retry.R) {
suite.client.RequireVersionChanged(r, fooProxyStateTemplate.Id, oldVersion)
})
@ -574,18 +625,20 @@ func (suite *xdsControllerTestSuite) TestController_ComputeAddUpdateDeleteLeafRe
_, ok := suite.leafCancels.Get(keyFromReference(fooLeafResRef))
require.False(r, ok)
})
})
}
// Sets up a full controller, and tests that reconciles are getting triggered for the leaf cert events it should.
// This test ensures that when a ProxyStateTemplate is deleted, the leaf watches are cancelled.
func (suite *xdsControllerTestSuite) TestController_ComputeLeafReferencesDeletePST() {
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
// Run the controller manager.
mgr := controller.NewManager(suite.client, suite.runtime.Logger)
mgr.Register(Controller(suite.mapper, suite.updater, suite.fetcher, suite.leafCertManager, suite.leafMapper, suite.leafCancels, "dc1"))
mgr.SetRaftLeader(true)
go mgr.Run(suite.ctx)
suite.setupFooProxyStateTemplateWithReferences()
suite.setupFooProxyStateTemplateWithReferences(tenancy)
leafCertRef := suite.fooLeafRefs["foo-workload-identity"]
fooLeafResRef := leafResourceRef(leafCertRef.Name, leafCertRef.Namespace, leafCertRef.Partition)
@ -601,8 +654,8 @@ func (suite *xdsControllerTestSuite) TestController_ComputeLeafReferencesDeleteP
// Assert that the leafs computed in the controller matches the expected leafs.
require.Len(r, actualLeafs, 1)
for k, l := range actualLeafs {
pem, _ := pem.Decode([]byte(l.Cert))
cert, err := x509.ParseCertificate(pem.Bytes)
pemDecode, _ := pem.Decode([]byte(l.Cert))
cert, err := x509.ParseCertificate(pemDecode.Bytes)
require.NoError(r, err)
require.Equal(r, cert.URIs[0].String(), suite.expectedFooProxyStateSpiffes[k])
// Check the state of the cancel functions map.
@ -616,17 +669,20 @@ func (suite *xdsControllerTestSuite) TestController_ComputeLeafReferencesDeleteP
req := &pbresource.DeleteRequest{
Id: suite.fooProxyStateTemplate.Id,
}
suite.client.Delete(suite.ctx, req)
_, err := suite.client.Delete(suite.ctx, req)
require.NoError(suite.T(), err)
// Ensure the leaf certificate watches were cancelled since we deleted the leaf reference.
retry.Run(suite.T(), func(r *retry.R) {
_, ok := suite.leafCancels.Get(keyFromReference(fooLeafResRef))
require.False(r, ok)
})
})
}
// Sets up a full controller, and tests that reconciles are getting triggered for the events it should.
func (suite *xdsControllerTestSuite) TestController_ComputeEndpointForProxyConnections() {
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
// Run the controller manager.
mgr := controller.NewManager(suite.client, suite.runtime.Logger)
@ -636,7 +692,7 @@ func (suite *xdsControllerTestSuite) TestController_ComputeEndpointForProxyConne
// Set up fooEndpoints and fooProxyStateTemplate with a reference to fooEndpoints. These need to be stored
// because the controller reconcile looks them up.
suite.setupFooProxyStateTemplateWithReferences()
suite.setupFooProxyStateTemplateWithReferences(tenancy)
// Assert that the expected ProxyState matches the actual ProxyState that PushChange was called with. This needs to
// be in a retry block unlike the Reconcile tests because the controller triggers asynchronously.
@ -654,6 +710,7 @@ func (suite *xdsControllerTestSuite) TestController_ComputeEndpointForProxyConne
// Wait for the proxy state template to be re-evaluated.
proxyStateTemp := suite.client.WaitForNewVersion(suite.T(), suite.fooProxyStateTemplate.Id, suite.fooProxyStateTemplate.Version)
require.NotNil(suite.T(), proxyStateTemp)
})
}
// Setup: fooProxyStateTemplate with:
@ -661,11 +718,14 @@ func (suite *xdsControllerTestSuite) TestController_ComputeEndpointForProxyConne
// - a LeafCertificateRef to "foo-workload-identity"
//
// Saves all related resources to the suite so they can be looked up by the controller or modified if needed.
func (suite *xdsControllerTestSuite) setupFooProxyStateTemplateWithReferences() {
func (suite *xdsControllerTestSuite) setupFooProxyStateTemplateWithReferences(tenancy *pbresource.Tenancy) {
fooService := resourcetest.Resource(pbcatalog.ServiceType, "foo-service").
WithData(suite.T(), &pbcatalog.Service{}).
WithTenancy(tenancy).
Write(suite.T(), suite.client)
suite.T().Cleanup(suite.deleteResourceFunc(fooService.Id))
fooEndpoints := resourcetest.Resource(pbcatalog.ServiceEndpointsType, "foo-service").
WithData(suite.T(), &pbcatalog.ServiceEndpoints{Endpoints: []*pbcatalog.Endpoint{
{
@ -687,9 +747,12 @@ func (suite *xdsControllerTestSuite) setupFooProxyStateTemplateWithReferences()
},
},
}}).
WithTenancy(tenancy).
WithOwner(fooService.Id).
Write(suite.T(), suite.client)
suite.T().Cleanup(suite.deleteResourceFunc(fooEndpoints.Id))
fooRequiredEndpoints := make(map[string]*pbproxystate.EndpointRef)
fooRequiredEndpoints["test-cluster-1"] = &pbproxystate.EndpointRef{
Id: fooEndpoints.Id,
@ -707,8 +770,11 @@ func (suite *xdsControllerTestSuite) setupFooProxyStateTemplateWithReferences()
RequiredLeafCertificates: fooRequiredLeafs,
ProxyState: &pbmesh.ProxyState{},
}).
WithTenancy(tenancy).
Write(suite.T(), suite.client)
suite.T().Cleanup(suite.deleteResourceFunc(fooProxyStateTemplate.Id))
retry.Run(suite.T(), func(r *retry.R) {
suite.client.RequireResourceExists(r, fooProxyStateTemplate.Id)
})
@ -754,6 +820,14 @@ func (suite *xdsControllerTestSuite) setupFooProxyStateTemplateWithReferences()
suite.expectedFooProxyStateEndpoints = expectedFooProxyStateEndpoints
suite.expectedTrustBundle = expectedTrustBundle
suite.expectedFooProxyStateSpiffes = expectedFooLeafSpiffes
suite.T().Cleanup(func() {
suite.fooEndpointRefs = make(map[string]*pbproxystate.EndpointRef)
suite.fooLeafRefs = make(map[string]*pbproxystate.LeafCertificateRef)
suite.expectedFooProxyStateEndpoints = make(map[string]*pbproxystate.Endpoints)
suite.expectedTrustBundle = make(map[string]*pbproxystate.TrustBundle)
suite.expectedFooProxyStateSpiffes = make(map[string]string)
})
}
// Setup:
@ -761,11 +835,14 @@ func (suite *xdsControllerTestSuite) setupFooProxyStateTemplateWithReferences()
// - barProxyStateTemplate with an EndpointsRef to fooBarEndpoints.
//
// Saves all related resources to the suite so they can be modified if needed.
func (suite *xdsControllerTestSuite) setupFooBarProxyStateTemplateAndEndpoints() {
func (suite *xdsControllerTestSuite) setupFooBarProxyStateTemplateAndEndpoints(tenancy *pbresource.Tenancy) {
fooService := resourcetest.Resource(pbcatalog.ServiceType, "foo-service").
WithData(suite.T(), &pbcatalog.Service{}).
WithTenancy(tenancy).
Write(suite.T(), suite.client)
suite.T().Cleanup(suite.deleteResourceFunc(fooService.Id))
fooEndpoints := resourcetest.Resource(pbcatalog.ServiceEndpointsType, "foo-service").
WithData(suite.T(), &pbcatalog.ServiceEndpoints{Endpoints: []*pbcatalog.Endpoint{
{
@ -788,12 +865,18 @@ func (suite *xdsControllerTestSuite) setupFooBarProxyStateTemplateAndEndpoints()
},
}}).
WithOwner(fooService.Id).
WithTenancy(tenancy).
Write(suite.T(), suite.client)
suite.T().Cleanup(suite.deleteResourceFunc(fooEndpoints.Id))
fooBarService := resourcetest.Resource(pbcatalog.ServiceType, "foo-bar-service").
WithData(suite.T(), &pbcatalog.Service{}).
WithTenancy(tenancy).
Write(suite.T(), suite.client)
suite.T().Cleanup(suite.deleteResourceFunc(fooBarService.Id))
fooBarEndpoints := resourcetest.Resource(pbcatalog.ServiceEndpointsType, "foo-bar-service").
WithData(suite.T(), &pbcatalog.ServiceEndpoints{Endpoints: []*pbcatalog.Endpoint{
{
@ -816,8 +899,11 @@ func (suite *xdsControllerTestSuite) setupFooBarProxyStateTemplateAndEndpoints()
},
}}).
WithOwner(fooBarService.Id).
WithTenancy(tenancy).
Write(suite.T(), suite.client)
suite.T().Cleanup(suite.deleteResourceFunc(fooBarEndpoints.Id))
fooRequiredEndpoints := make(map[string]*pbproxystate.EndpointRef)
fooRequiredEndpoints["test-cluster-1"] = &pbproxystate.EndpointRef{
Id: fooEndpoints.Id,
@ -841,8 +927,11 @@ func (suite *xdsControllerTestSuite) setupFooBarProxyStateTemplateAndEndpoints()
RequiredEndpoints: fooRequiredEndpoints,
ProxyState: &pbmesh.ProxyState{},
}).
WithTenancy(tenancy).
Write(suite.T(), suite.client)
suite.T().Cleanup(suite.deleteResourceFunc(fooProxyStateTemplate.Id))
retry.Run(suite.T(), func(r *retry.R) {
suite.client.RequireResourceExists(r, fooProxyStateTemplate.Id)
})
@ -853,8 +942,11 @@ func (suite *xdsControllerTestSuite) setupFooBarProxyStateTemplateAndEndpoints()
RequiredEndpoints: barRequiredEndpoints,
ProxyState: &pbmesh.ProxyState{},
}).
WithTenancy(tenancy).
Write(suite.T(), suite.client)
suite.T().Cleanup(suite.deleteResourceFunc(barProxyStateTemplate.Id))
retry.Run(suite.T(), func(r *retry.R) {
suite.client.RequireResourceExists(r, barProxyStateTemplate.Id)
})
@ -935,9 +1027,17 @@ func (suite *xdsControllerTestSuite) setupFooBarProxyStateTemplateAndEndpoints()
suite.fooBarService = fooBarService
suite.expectedFooProxyStateEndpoints = expectedFooProxyStateEndpoints
suite.expectedBarProxyStateEndpoints = expectedBarProxyStateEndpoints
suite.T().Cleanup(func() {
suite.barEndpointRefs = make(map[string]*pbproxystate.EndpointRef)
suite.fooEndpointRefs = make(map[string]*pbproxystate.EndpointRef)
suite.expectedFooProxyStateEndpoints = make(map[string]*pbproxystate.Endpoints)
suite.expectedBarProxyStateEndpoints = make(map[string]*pbproxystate.Endpoints)
})
}
func (suite *xdsControllerTestSuite) TestReconcile_prevWatchesToCancel() {
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
makeRef := func(names ...string) []*pbresource.Reference {
out := make([]*pbresource.Reference, len(names))
for i, name := range names {
@ -948,7 +1048,7 @@ func (suite *xdsControllerTestSuite) TestReconcile_prevWatchesToCancel() {
GroupVersion: "v",
Kind: "k",
},
Tenancy: &pbresource.Tenancy{},
Tenancy: tenancy,
}
}
return out
@ -997,6 +1097,7 @@ func (suite *xdsControllerTestSuite) TestReconcile_prevWatchesToCancel() {
toCancel := prevWatchesToCancel(tc.old, convert(tc.new))
require.ElementsMatch(suite.T(), toCancel, tc.expect)
}
})
}
func TestXdsController(t *testing.T) {
@ -1017,6 +1118,7 @@ func TestXdsController(t *testing.T) {
// rather than just endpoints, leaf certs, and trust bundles, the test also ensures
// side effects or change in scope to XDS controller are not introduce mistakenly.
func (suite *xdsControllerTestSuite) TestReconcile_SidecarProxyGoldenFileInputs() {
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
path := "../sidecarproxy/builder/testdata"
cases := []string{
// destinations - please add in alphabetical order
@ -1053,14 +1155,17 @@ func (suite *xdsControllerTestSuite) TestReconcile_SidecarProxyGoldenFileInputs(
// Destinations will need endpoint refs set up.
if strings.Split(name, "/")[0] == "destination" && len(pst.ProxyState.Endpoints) == 0 {
suite.addRequiredEndpointsAndRefs(pst)
suite.addRequiredEndpointsAndRefs(pst, tenancy)
}
// Store the initial ProxyStateTemplate.
proxyStateTemplate := resourcetest.Resource(pbmesh.ProxyStateTemplateType, "test").
WithData(suite.T(), pst).
WithTenancy(tenancy).
Write(suite.T(), suite.client)
suite.T().Cleanup(suite.deleteResourceFunc(proxyStateTemplate.Id))
// Check with resource service that it exists.
retry.Run(suite.T(), func(r *retry.R) {
suite.client.RequireResourceExists(r, proxyStateTemplate.Id)
@ -1095,9 +1200,10 @@ func (suite *xdsControllerTestSuite) TestReconcile_SidecarProxyGoldenFileInputs(
require.JSONEq(suite.T(), expected, actual)
})
}
})
}
func (suite *xdsControllerTestSuite) addRequiredEndpointsAndRefs(pst *pbmesh.ProxyStateTemplate) {
func (suite *xdsControllerTestSuite) addRequiredEndpointsAndRefs(pst *pbmesh.ProxyStateTemplate, tenancy *pbresource.Tenancy) {
//get service data
serviceData := &pbcatalog.Service{}
var vp uint32 = 7000
@ -1125,8 +1231,11 @@ func (suite *xdsControllerTestSuite) addRequiredEndpointsAndRefs(pst *pbmesh.Pro
// create service.
svc := resourcetest.Resource(pbcatalog.ServiceType, svcName).
WithData(suite.T(), &pbcatalog.Service{}).
WithTenancy(tenancy).
Write(suite.T(), suite.client)
suite.T().Cleanup(suite.deleteResourceFunc(svc.Id))
// create endpoints with svc as owner.
eps := resourcetest.Resource(pbcatalog.ServiceEndpointsType, svcName).
WithData(suite.T(), &pbcatalog.ServiceEndpoints{Endpoints: []*pbcatalog.Endpoint{
@ -1146,8 +1255,11 @@ func (suite *xdsControllerTestSuite) addRequiredEndpointsAndRefs(pst *pbmesh.Pro
},
}}).
WithOwner(svc.Id).
WithTenancy(tenancy).
Write(suite.T(), suite.client)
suite.T().Cleanup(suite.deleteResourceFunc(eps.Id))
// add to working list of required endpoints.
requiredEps[clusterName] = &pbproxystate.EndpointRef{
Id: eps.Id,
@ -1167,3 +1279,21 @@ func JSONToProxyTemplate(t *testing.T, json []byte) *pbmesh.ProxyStateTemplate {
require.NoError(t, err)
return proxyTemplate
}
func (suite *xdsControllerTestSuite) runTestCaseWithTenancies(testCase func(tenancy *pbresource.Tenancy)) {
for _, tenancy := range suite.tenancies {
suite.Run(suite.appendTenancyInfo(tenancy), func() {
testCase(tenancy)
})
}
}
func (suite *xdsControllerTestSuite) appendTenancyInfo(tenancy *pbresource.Tenancy) string {
return fmt.Sprintf("%s_Namespace_%s_Partition", tenancy.Namespace, tenancy.Partition)
}
func (suite *xdsControllerTestSuite) deleteResourceFunc(id *pbresource.ID) func() {
return func() {
suite.client.MustDelete(suite.T(), id)
}
}

View File

@ -4,10 +4,10 @@
package resourcetest
import (
"github.com/hashicorp/consul/agent/structs"
"strings"
"testing"
"github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/internal/resource"
"github.com/hashicorp/consul/proto-public/pbresource"
)