diff --git a/internal/mesh/internal/controllers/sidecarproxy/controller_test.go b/internal/mesh/internal/controllers/sidecarproxy/controller_test.go index 0d6684f0ef54..ac15370ccb49 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/controller_test.go +++ b/internal/mesh/internal/controllers/sidecarproxy/controller_test.go @@ -5,6 +5,9 @@ package sidecarproxy import ( "context" + "fmt" + mockres "github.com/hashicorp/consul/agent/grpc-external/services/resource" + "github.com/hashicorp/consul/internal/mesh/internal/controllers/routes/routestest" "strings" "testing" @@ -16,7 +19,6 @@ import ( "github.com/hashicorp/consul/internal/auth" "github.com/hashicorp/consul/internal/catalog" "github.com/hashicorp/consul/internal/controller" - "github.com/hashicorp/consul/internal/mesh/internal/controllers/routes/routestest" "github.com/hashicorp/consul/internal/mesh/internal/controllers/sidecarproxy/builder" "github.com/hashicorp/consul/internal/mesh/internal/controllers/sidecarproxy/cache" "github.com/hashicorp/consul/internal/mesh/internal/controllers/sidecarproxy/fetcher" @@ -59,10 +61,22 @@ type controllerTestSuite struct { dbEndpointsData *pbcatalog.ServiceEndpoints proxyStateTemplate *pbmesh.ProxyStateTemplate + tenancies []*pbresource.Tenancy } func (suite *controllerTestSuite) SetupTest() { - resourceClient := svctest.RunResourceService(suite.T(), types.Register, catalog.RegisterTypes, auth.RegisterTypes) + suite.tenancies = resourcetest.TestTenancies() + mockTenancyBridge := &mockres.MockTenancyBridge{} + for _, tenancy := range suite.tenancies { + mockTenancyBridge.On("PartitionExists", tenancy.Partition).Return(true, nil) + mockTenancyBridge.On("NamespaceExists", tenancy.Partition, tenancy.Namespace).Return(true, nil) + mockTenancyBridge.On("IsPartitionMarkedForDeletion", tenancy.Partition).Return(false, nil) + mockTenancyBridge.On("IsNamespaceMarkedForDeletion", tenancy.Partition, tenancy.Namespace).Return(false, nil) + } + cfg := mockres.Config{ + TenancyBridge: mockTenancyBridge, + } + resourceClient := svctest.RunResourceServiceWithConfig(suite.T(), cfg, types.Register, catalog.RegisterTypes, auth.RegisterTypes) suite.client = resourcetest.NewClient(resourceClient) suite.runtime = controller.Runtime{Client: resourceClient, Logger: testutil.Logger(suite.T())} suite.ctx = testutil.TestContext(suite.T()) @@ -74,67 +88,17 @@ func (suite *controllerTestSuite) SetupTest() { }, } - { - // DB will be a service with a single workload, IN the mesh that will - // be a destination of web. - - suite.dbWorkload = &pbcatalog.Workload{ - Identity: "db-identity", - Addresses: []*pbcatalog.WorkloadAddress{ - {Host: "10.0.4.1"}, - }, - Ports: map[string]*pbcatalog.WorkloadPort{ - "http": {Port: 8080, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, - "mesh": {Port: 20000, Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, - }, - } - suite.dbWorkloadID = resourcetest.Resource(pbcatalog.WorkloadType, "db-abc"). - WithData(suite.T(), suite.dbWorkload). - Write(suite.T(), resourceClient).Id - - suite.dbService = resourcetest.Resource(pbcatalog.ServiceType, "db-service"). - WithData(suite.T(), &pbcatalog.Service{ - Workloads: &pbcatalog.WorkloadSelector{Names: []string{"db-abc"}}, - VirtualIps: []string{"1.1.1.1"}, - Ports: []*pbcatalog.ServicePort{ - {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, - {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, - }}). - Write(suite.T(), suite.client) - - suite.dbEndpointsData = &pbcatalog.ServiceEndpoints{ - Endpoints: []*pbcatalog.Endpoint{ - { - TargetRef: suite.dbWorkloadID, - Addresses: suite.dbWorkload.Addresses, - Ports: suite.dbWorkload.Ports, - Identity: "db-identity", - }, - }, - } - suite.dbEndpoints = resourcetest.Resource(pbcatalog.ServiceEndpointsType, "db-service"). - WithData(suite.T(), suite.dbEndpointsData). - Write(suite.T(), suite.client) - - } - - suite.apiWorkload = &pbcatalog.Workload{ - Identity: "api-identity", + suite.dbWorkload = &pbcatalog.Workload{ + Identity: "db-identity", Addresses: []*pbcatalog.WorkloadAddress{ - { - Host: "10.0.0.1", - }, + {Host: "10.0.4.1"}, }, Ports: map[string]*pbcatalog.WorkloadPort{ - "tcp": {Port: 8080, Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, + "http": {Port: 8080, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, "mesh": {Port: 20000, Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, }, } - suite.apiWorkloadID = resourcetest.Resource(pbcatalog.WorkloadType, "api-abc"). - WithData(suite.T(), suite.apiWorkload). - Write(suite.T(), resourceClient).Id - suite.apiServiceData = &pbcatalog.Service{ Workloads: &pbcatalog.WorkloadSelector{Names: []string{"api-abc"}}, VirtualIps: []string{"1.1.1.1"}, @@ -159,13 +123,83 @@ func (suite *controllerTestSuite) SetupTest() { }, }, } +} + +func (suite *controllerTestSuite) setupSuiteWithTenancy(tenancy *pbresource.Tenancy) { + + suite.apiWorkload = &pbcatalog.Workload{ + Identity: "api-identity", + Addresses: []*pbcatalog.WorkloadAddress{ + { + Host: "10.0.0.1", + }, + }, + Ports: map[string]*pbcatalog.WorkloadPort{ + "tcp": {Port: 8080, Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, + "mesh": {Port: 20000, Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + }, + } + + webWorkloadData := &pbcatalog.Workload{ + Identity: "web-identity", + Addresses: []*pbcatalog.WorkloadAddress{ + { + Host: "10.0.0.2", + }, + }, + Ports: map[string]*pbcatalog.WorkloadPort{ + "tcp": {Port: 8081, Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, + "mesh": {Port: 20000, Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + }, + } + // DB will be a service with a single workload, IN the mesh that will + // be a destination of web. + + suite.dbWorkloadID = resourcetest.Resource(pbcatalog.WorkloadType, "db-abc"). + WithData(suite.T(), suite.dbWorkload). + WithTenancy(tenancy). + Write(suite.T(), suite.client.ResourceServiceClient).Id + + suite.dbService = resourcetest.Resource(pbcatalog.ServiceType, "db-service"). + WithData(suite.T(), &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{Names: []string{"db-abc"}}, + VirtualIps: []string{"1.1.1.1"}, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + }}). + WithTenancy(tenancy). + Write(suite.T(), suite.client) + + suite.dbEndpointsData = &pbcatalog.ServiceEndpoints{ + Endpoints: []*pbcatalog.Endpoint{ + { + TargetRef: suite.dbWorkloadID, + Addresses: suite.dbWorkload.Addresses, + Ports: suite.dbWorkload.Ports, + Identity: "db-identity", + }, + }, + } + + suite.dbEndpoints = resourcetest.Resource(pbcatalog.ServiceEndpointsType, "db-service"). + WithData(suite.T(), suite.dbEndpointsData). + WithTenancy(tenancy). + Write(suite.T(), suite.client) + + suite.apiWorkloadID = resourcetest.Resource(pbcatalog.WorkloadType, "api-abc"). + WithTenancy(tenancy). + WithData(suite.T(), suite.apiWorkload). + Write(suite.T(), suite.client.ResourceServiceClient).Id suite.apiComputedTrafficPermissions = resourcetest.Resource(pbauth.ComputedTrafficPermissionsType, suite.apiWorkload.Identity). WithData(suite.T(), suite.apiComputedTrafficPermissionsData). - Write(suite.T(), resourceClient) + WithTenancy(tenancy). + Write(suite.T(), suite.client.ResourceServiceClient) suite.apiService = resourcetest.Resource(pbcatalog.ServiceType, "api-service"). WithData(suite.T(), suite.apiServiceData). + WithTenancy(tenancy). Write(suite.T(), suite.client.ResourceServiceClient) suite.apiEndpointsData = &pbcatalog.ServiceEndpoints{ @@ -178,31 +212,24 @@ func (suite *controllerTestSuite) SetupTest() { }, }, } + suite.apiEndpoints = resourcetest.Resource(pbcatalog.ServiceEndpointsType, "api-service"). WithData(suite.T(), suite.apiEndpointsData). + WithTenancy(tenancy). Write(suite.T(), suite.client.ResourceServiceClient) - webWorkloadData := &pbcatalog.Workload{ - Identity: "web-identity", - Addresses: []*pbcatalog.WorkloadAddress{ - { - Host: "10.0.0.2", - }, - }, - Ports: map[string]*pbcatalog.WorkloadPort{ - "tcp": {Port: 8081, Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, - "mesh": {Port: 20000, Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, - }, - } suite.webWorkload = resourcetest.Resource(pbcatalog.WorkloadType, "web-def"). WithData(suite.T(), webWorkloadData). + WithTenancy(tenancy). Write(suite.T(), suite.client) resourcetest.Resource(pbauth.ComputedTrafficPermissionsType, webWorkloadData.Identity). WithData(suite.T(), &pbauth.ComputedTrafficPermissions{IsDefault: true}). - Write(suite.T(), resourceClient) + WithTenancy(tenancy). + Write(suite.T(), suite.client.ResourceServiceClient) resourcetest.Resource(pbcatalog.ServiceType, "web"). + WithTenancy(tenancy). WithData(suite.T(), &pbcatalog.Service{ Workloads: &pbcatalog.WorkloadSelector{Names: []string{"web-def"}}, Ports: []*pbcatalog.ServicePort{ @@ -212,6 +239,7 @@ func (suite *controllerTestSuite) SetupTest() { Write(suite.T(), suite.client) resourcetest.Resource(pbcatalog.ServiceEndpointsType, "web"). + WithTenancy(tenancy). WithData(suite.T(), &pbcatalog.ServiceEndpoints{ Endpoints: []*pbcatalog.Endpoint{ { @@ -236,268 +264,290 @@ func (suite *controllerTestSuite) SetupTest() { } func (suite *controllerTestSuite) TestWorkloadPortProtocolsFromService_NoServicesInCache() { - dataFetcher := fetcher.New(suite.client, suite.ctl.cache) + suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) { + dataFetcher := fetcher.New(suite.client, suite.ctl.cache) - workload := resourcetest.Resource(pbcatalog.WorkloadType, "api-workload"). - WithData(suite.T(), &pbcatalog.Workload{ - Ports: map[string]*pbcatalog.WorkloadPort{ - "tcp": {Port: 8080}, - }, - }). - Build() + workload := resourcetest.Resource(pbcatalog.WorkloadType, "api-workload"). + WithTenancy(tenancy). + WithData(suite.T(), &pbcatalog.Workload{ + Ports: map[string]*pbcatalog.WorkloadPort{ + "tcp": {Port: 8080}, + }, + }). + Build() - decWorkload := resourcetest.MustDecode[*pbcatalog.Workload](suite.T(), workload) - workloadPorts, err := suite.ctl.workloadPortProtocolsFromService(suite.ctx, dataFetcher, decWorkload, suite.runtime.Logger) - require.NoError(suite.T(), err) - prototest.AssertDeepEqual(suite.T(), pbcatalog.Protocol_PROTOCOL_TCP, workloadPorts["tcp"].GetProtocol()) + decWorkload := resourcetest.MustDecode[*pbcatalog.Workload](suite.T(), workload) + workloadPorts, err := suite.ctl.workloadPortProtocolsFromService(suite.ctx, dataFetcher, decWorkload, suite.runtime.Logger) + require.NoError(suite.T(), err) + prototest.AssertDeepEqual(suite.T(), pbcatalog.Protocol_PROTOCOL_TCP, workloadPorts["tcp"].GetProtocol()) + }) } func (suite *controllerTestSuite) TestWorkloadPortProtocolsFromService_ServiceNotFound() { - c := cache.New() - dataFetcher := fetcher.New(suite.client, c) - ctrl := &reconciler{ - cache: c, - getTrustDomain: func() (string, error) { - return "test.consul", nil - }, - } - svc := resourcetest.Resource(pbcatalog.ServiceType, "not-found"). - WithData(suite.T(), &pbcatalog.Service{ - Workloads: &pbcatalog.WorkloadSelector{ - Names: []string{"api-workload"}, + suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) { + c := cache.New() + dataFetcher := fetcher.New(suite.client, c) + ctrl := &reconciler{ + cache: c, + getTrustDomain: func() (string, error) { + return "test.consul", nil }, - }). - Build() + } + svc := resourcetest.Resource(pbcatalog.ServiceType, "not-found"). + WithData(suite.T(), &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Names: []string{"api-workload"}, + }, + }). + WithTenancy(tenancy). + Build() - decSvc := resourcetest.MustDecode[*pbcatalog.Service](suite.T(), svc) - c.TrackService(decSvc) + decSvc := resourcetest.MustDecode[*pbcatalog.Service](suite.T(), svc) + c.TrackService(decSvc) - workload := resourcetest.Resource(pbcatalog.WorkloadType, "api-workload"). - WithData(suite.T(), &pbcatalog.Workload{ - Ports: map[string]*pbcatalog.WorkloadPort{ - "tcp": {Port: 8080}, - }, - }). - Build() + workload := resourcetest.Resource(pbcatalog.WorkloadType, "api-workload"). + WithData(suite.T(), &pbcatalog.Workload{ + Ports: map[string]*pbcatalog.WorkloadPort{ + "tcp": {Port: 8080}, + }, + }). + WithTenancy(tenancy). + Build() - decWorkload := resourcetest.MustDecode[*pbcatalog.Workload](suite.T(), workload) + decWorkload := resourcetest.MustDecode[*pbcatalog.Workload](suite.T(), workload) - workloadPorts, err := ctrl.workloadPortProtocolsFromService(suite.ctx, dataFetcher, decWorkload, suite.runtime.Logger) - require.NoError(suite.T(), err) - prototest.AssertDeepEqual(suite.T(), pbcatalog.Protocol_PROTOCOL_TCP, workloadPorts["tcp"].GetProtocol()) - // Check that the service is no longer in cache. - require.Nil(suite.T(), c.ServicesForWorkload(workload.Id)) + workloadPorts, err := ctrl.workloadPortProtocolsFromService(suite.ctx, dataFetcher, decWorkload, suite.runtime.Logger) + require.NoError(suite.T(), err) + prototest.AssertDeepEqual(suite.T(), pbcatalog.Protocol_PROTOCOL_TCP, workloadPorts["tcp"].GetProtocol()) + // Check that the service is no longer in cache. + require.Nil(suite.T(), c.ServicesForWorkload(workload.Id)) + }) } func (suite *controllerTestSuite) TestWorkloadPortProtocolsFromService() { - c := cache.New() - dataFetcher := fetcher.New(suite.client, c) - ctrl := &reconciler{ - cache: c, - getTrustDomain: func() (string, error) { - return "test.consul", nil - }, - } - svc1 := resourcetest.Resource(pbcatalog.ServiceType, "api-1"). - WithData(suite.T(), &pbcatalog.Service{ - Workloads: &pbcatalog.WorkloadSelector{ - Names: []string{"api-workload"}, + suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) { + c := cache.New() + dataFetcher := fetcher.New(suite.client, c) + ctrl := &reconciler{ + cache: c, + getTrustDomain: func() (string, error) { + return "test.consul", nil }, - Ports: []*pbcatalog.ServicePort{ - { - TargetPort: "http1", - Protocol: pbcatalog.Protocol_PROTOCOL_HTTP, + } + svc1 := resourcetest.Resource(pbcatalog.ServiceType, "api-1"). + WithData(suite.T(), &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Names: []string{"api-workload"}, }, - { - TargetPort: "conflict", - Protocol: pbcatalog.Protocol_PROTOCOL_HTTP, + Ports: []*pbcatalog.ServicePort{ + { + TargetPort: "http1", + Protocol: pbcatalog.Protocol_PROTOCOL_HTTP, + }, + { + TargetPort: "conflict", + Protocol: pbcatalog.Protocol_PROTOCOL_HTTP, + }, }, - }, - }). - Write(suite.T(), suite.client) + }). + WithTenancy(tenancy). + Write(suite.T(), suite.client) - decSvc := resourcetest.MustDecode[*pbcatalog.Service](suite.T(), svc1) - c.TrackService(decSvc) + decSvc := resourcetest.MustDecode[*pbcatalog.Service](suite.T(), svc1) + c.TrackService(decSvc) - svc2 := resourcetest.Resource(pbcatalog.ServiceType, "api-2"). - WithData(suite.T(), &pbcatalog.Service{ - Workloads: &pbcatalog.WorkloadSelector{ - Names: []string{"api-workload"}, - }, - Ports: []*pbcatalog.ServicePort{ - { - TargetPort: "http2", - Protocol: pbcatalog.Protocol_PROTOCOL_HTTP2, + svc2 := resourcetest.Resource(pbcatalog.ServiceType, "api-2"). + WithTenancy(tenancy). + WithData(suite.T(), &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Names: []string{"api-workload"}, }, - { - TargetPort: "conflict", - Protocol: pbcatalog.Protocol_PROTOCOL_GRPC, + Ports: []*pbcatalog.ServicePort{ + { + TargetPort: "http2", + Protocol: pbcatalog.Protocol_PROTOCOL_HTTP2, + }, + { + TargetPort: "conflict", + Protocol: pbcatalog.Protocol_PROTOCOL_GRPC, + }, }, - }, - }). - Write(suite.T(), suite.client) + }). + Write(suite.T(), suite.client) - decSvc = resourcetest.MustDecode[*pbcatalog.Service](suite.T(), svc2) - c.TrackService(decSvc) + decSvc = resourcetest.MustDecode[*pbcatalog.Service](suite.T(), svc2) + c.TrackService(decSvc) - workload := resourcetest.Resource(pbcatalog.WorkloadType, "api-workload"). - WithData(suite.T(), &pbcatalog.Workload{ - Ports: map[string]*pbcatalog.WorkloadPort{ - "http1": {Port: 8080}, - "http2": {Port: 9090}, - "conflict": {Port: 9091}, - "not-selected": {Port: 8081}, - "specified-protocol": {Port: 8082, Protocol: pbcatalog.Protocol_PROTOCOL_GRPC}, - "mesh": {Port: 20000, Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, - }, - }). - WithTenancy(resource.DefaultNamespacedTenancy()). - Build() + workload := resourcetest.Resource(pbcatalog.WorkloadType, "api-workload"). + WithTenancy(tenancy). + WithData(suite.T(), &pbcatalog.Workload{ + Ports: map[string]*pbcatalog.WorkloadPort{ + "http1": {Port: 8080}, + "http2": {Port: 9090}, + "conflict": {Port: 9091}, + "not-selected": {Port: 8081}, + "specified-protocol": {Port: 8082, Protocol: pbcatalog.Protocol_PROTOCOL_GRPC}, + "mesh": {Port: 20000, Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + }, + }). + WithTenancy(tenancy). + Build() - decWorkload := resourcetest.MustDecode[*pbcatalog.Workload](suite.T(), workload) + decWorkload := resourcetest.MustDecode[*pbcatalog.Workload](suite.T(), workload) - expWorkloadPorts := map[string]*pbcatalog.WorkloadPort{ - // This protocol should be inherited from service 1. - "http1": {Port: 8080, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + expWorkloadPorts := map[string]*pbcatalog.WorkloadPort{ + // This protocol should be inherited from service 1. + "http1": {Port: 8080, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, - // this protocol should be inherited from service 2. - "http2": {Port: 9090, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP2}, + // this protocol should be inherited from service 2. + "http2": {Port: 9090, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP2}, - // This port is not selected by the service and should default to tcp. - "not-selected": {Port: 8081, Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, + // This port is not selected by the service and should default to tcp. + "not-selected": {Port: 8081, Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, - // This port has conflicting protocols in each service and so it should default to tcp. - "conflict": {Port: 9091, Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, + // This port has conflicting protocols in each service and so it should default to tcp. + "conflict": {Port: 9091, Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, - // These port should keep its existing protocol. - "specified-protocol": {Port: 8082, Protocol: pbcatalog.Protocol_PROTOCOL_GRPC}, - "mesh": {Port: 20000, Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, - } + // These port should keep its existing protocol. + "specified-protocol": {Port: 8082, Protocol: pbcatalog.Protocol_PROTOCOL_GRPC}, + "mesh": {Port: 20000, Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + } - workloadPorts, err := ctrl.workloadPortProtocolsFromService(suite.ctx, dataFetcher, decWorkload, suite.runtime.Logger) - require.NoError(suite.T(), err) + workloadPorts, err := ctrl.workloadPortProtocolsFromService(suite.ctx, dataFetcher, decWorkload, suite.runtime.Logger) + require.NoError(suite.T(), err) - prototest.AssertDeepEqual(suite.T(), expWorkloadPorts, workloadPorts) + prototest.AssertDeepEqual(suite.T(), expWorkloadPorts, workloadPorts) + }) } func (suite *controllerTestSuite) TestReconcile_NoWorkload() { - // This test ensures that removed workloads are ignored and don't result - // in the creation of the proxy state template. - err := suite.ctl.Reconcile(context.Background(), suite.runtime, controller.Request{ - ID: resourceID(pbmesh.ProxyStateTemplateType, "not-found"), - }) - require.NoError(suite.T(), err) + suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) { + // This test ensures that removed workloads are ignored and don't result + // in the creation of the proxy state template. + err := suite.ctl.Reconcile(context.Background(), suite.runtime, controller.Request{ + ID: resourceID(pbmesh.ProxyStateTemplateType, "not-found", tenancy), + }) + require.NoError(suite.T(), err) - suite.client.RequireResourceNotFound(suite.T(), resourceID(pbmesh.ProxyStateTemplateType, "not-found")) + suite.client.RequireResourceNotFound(suite.T(), resourceID(pbmesh.ProxyStateTemplateType, "not-found", tenancy)) + }) } func (suite *controllerTestSuite) TestReconcile_NonMeshWorkload() { - // This test ensures that non-mesh workloads are ignored by the controller. + suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) { + // This test ensures that non-mesh workloads are ignored by the controller. - nonMeshWorkload := &pbcatalog.Workload{ - Addresses: []*pbcatalog.WorkloadAddress{ - { - Host: "10.0.0.1", + nonMeshWorkload := &pbcatalog.Workload{ + Addresses: []*pbcatalog.WorkloadAddress{ + { + Host: "10.0.0.1", + }, }, - }, - Ports: map[string]*pbcatalog.WorkloadPort{ - "tcp": {Port: 8080, Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, - }, - } + Ports: map[string]*pbcatalog.WorkloadPort{ + "tcp": {Port: 8080, Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, + }, + } - resourcetest.Resource(pbcatalog.WorkloadType, "test-non-mesh-api-workload"). - WithData(suite.T(), nonMeshWorkload). - Write(suite.T(), suite.client.ResourceServiceClient) + resourcetest.Resource(pbcatalog.WorkloadType, "test-non-mesh-api-workload"). + WithData(suite.T(), nonMeshWorkload). + WithTenancy(tenancy). + Write(suite.T(), suite.client.ResourceServiceClient) - err := suite.ctl.Reconcile(context.Background(), suite.runtime, controller.Request{ - ID: resourceID(pbmesh.ProxyStateTemplateType, "test-non-mesh-api-workload"), - }) + err := suite.ctl.Reconcile(context.Background(), suite.runtime, controller.Request{ + ID: resourceID(pbmesh.ProxyStateTemplateType, "test-non-mesh-api-workload", tenancy), + }) - require.NoError(suite.T(), err) - suite.client.RequireResourceNotFound(suite.T(), resourceID(pbmesh.ProxyStateTemplateType, "test-non-mesh-api-workload")) + require.NoError(suite.T(), err) + suite.client.RequireResourceNotFound(suite.T(), resourceID(pbmesh.ProxyStateTemplateType, "test-non-mesh-api-workload", tenancy)) + }) } func (suite *controllerTestSuite) TestReconcile_NoExistingProxyStateTemplate() { - err := suite.ctl.Reconcile(context.Background(), suite.runtime, controller.Request{ - ID: resourceID(pbmesh.ProxyStateTemplateType, suite.apiWorkloadID.Name), - }) - require.NoError(suite.T(), err) + suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) { - res := suite.client.RequireResourceExists(suite.T(), resourceID(pbmesh.ProxyStateTemplateType, suite.apiWorkloadID.Name)) - require.NoError(suite.T(), err) - require.NotNil(suite.T(), res.Data) - prototest.AssertDeepEqual(suite.T(), suite.apiWorkloadID, res.Owner) + err := suite.ctl.Reconcile(context.Background(), suite.runtime, controller.Request{ + ID: resourceID(pbmesh.ProxyStateTemplateType, suite.apiWorkloadID.Name, tenancy), + }) + require.NoError(suite.T(), err) + + res := suite.client.RequireResourceExists(suite.T(), resourceID(pbmesh.ProxyStateTemplateType, suite.apiWorkloadID.Name, tenancy)) + require.NoError(suite.T(), err) + require.NotNil(suite.T(), res.Data) + prototest.AssertDeepEqual(suite.T(), suite.apiWorkloadID, res.Owner) + }) } func (suite *controllerTestSuite) TestReconcile_ExistingProxyStateTemplate_WithUpdates() { - // This test ensures that we write a new proxy state template when there are changes. - - // Write the original. - resourcetest.Resource(pbmesh.ProxyStateTemplateType, "api-abc"). - WithData(suite.T(), suite.proxyStateTemplate). - WithOwner(suite.apiWorkloadID). - Write(suite.T(), suite.client.ResourceServiceClient) + suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) { + // This test ensures that we write a new proxy state template when there are changes. + + // Write the original. + resourcetest.Resource(pbmesh.ProxyStateTemplateType, "api-abc"). + WithData(suite.T(), suite.proxyStateTemplate). + WithOwner(suite.apiWorkloadID). + WithTenancy(tenancy). + Write(suite.T(), suite.client.ResourceServiceClient) - // Update the apiWorkload and check that we default the port to tcp if it's unspecified. - suite.apiWorkload.Ports["tcp"].Protocol = pbcatalog.Protocol_PROTOCOL_UNSPECIFIED + // Update the apiWorkload and check that we default the port to tcp if it's unspecified. + suite.apiWorkload.Ports["tcp"].Protocol = pbcatalog.Protocol_PROTOCOL_UNSPECIFIED - updatedWorkloadID := resourcetest.Resource(pbcatalog.WorkloadType, "api-abc"). - WithData(suite.T(), suite.apiWorkload). - Write(suite.T(), suite.client.ResourceServiceClient).Id + updatedWorkloadID := resourcetest.Resource(pbcatalog.WorkloadType, "api-abc"). + WithTenancy(tenancy). + WithData(suite.T(), suite.apiWorkload). + Write(suite.T(), suite.client.ResourceServiceClient).Id - err := suite.ctl.Reconcile(context.Background(), suite.runtime, controller.Request{ - ID: resourceID(pbmesh.ProxyStateTemplateType, updatedWorkloadID.Name), - }) - require.NoError(suite.T(), err) + err := suite.ctl.Reconcile(context.Background(), suite.runtime, controller.Request{ + ID: resourceID(pbmesh.ProxyStateTemplateType, updatedWorkloadID.Name, tenancy), + }) + require.NoError(suite.T(), err) - res := suite.client.RequireResourceExists(suite.T(), resourceID(pbmesh.ProxyStateTemplateType, updatedWorkloadID.Name)) - require.NoError(suite.T(), err) - require.NotNil(suite.T(), res.Data) - prototest.AssertDeepEqual(suite.T(), updatedWorkloadID, res.Owner) + res := suite.client.RequireResourceExists(suite.T(), resourceID(pbmesh.ProxyStateTemplateType, updatedWorkloadID.Name, tenancy)) + require.NoError(suite.T(), err) + require.NotNil(suite.T(), res.Data) + prototest.AssertDeepEqual(suite.T(), updatedWorkloadID, res.Owner) - var updatedProxyStateTemplate pbmesh.ProxyStateTemplate - err = res.Data.UnmarshalTo(&updatedProxyStateTemplate) - require.NoError(suite.T(), err) + var updatedProxyStateTemplate pbmesh.ProxyStateTemplate + err = res.Data.UnmarshalTo(&updatedProxyStateTemplate) + require.NoError(suite.T(), err) - // Check that our value is updated in the proxy state template. - require.Len(suite.T(), updatedProxyStateTemplate.ProxyState.Listeners, 1) - require.Len(suite.T(), updatedProxyStateTemplate.ProxyState.Listeners[0].Routers, 1) + // Check that our value is updated in the proxy state template. + require.Len(suite.T(), updatedProxyStateTemplate.ProxyState.Listeners, 1) + require.Len(suite.T(), updatedProxyStateTemplate.ProxyState.Listeners[0].Routers, 1) - l4InboundRouter := updatedProxyStateTemplate.ProxyState.Listeners[0]. - Routers[0].GetL4() - require.NotNil(suite.T(), l4InboundRouter) + l4InboundRouter := updatedProxyStateTemplate.ProxyState.Listeners[0]. + Routers[0].GetL4() + require.NotNil(suite.T(), l4InboundRouter) + }) } func (suite *controllerTestSuite) TestReconcile_ExistingProxyStateTemplate_NoUpdates() { - // This test ensures that we skip writing of the proxy state template when there are no changes to it. + suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) { + // This test ensures that we skip writing of the proxy state template when there are no changes to it. + + // Write the original. + originalProxyState := resourcetest.Resource(pbmesh.ProxyStateTemplateType, "api-abc"). + WithData(suite.T(), suite.proxyStateTemplate). + WithOwner(suite.apiWorkloadID). + WithTenancy(tenancy). + Write(suite.T(), suite.client.ResourceServiceClient) - // Write the original. - originalProxyState := resourcetest.Resource(pbmesh.ProxyStateTemplateType, "api-abc"). - WithData(suite.T(), suite.proxyStateTemplate). - WithOwner(suite.apiWorkloadID). - Write(suite.T(), suite.client.ResourceServiceClient) + // Update the metadata on the apiWorkload which should result in no changes. + updatedWorkloadID := resourcetest.Resource(pbcatalog.WorkloadType, "api-abc"). + WithData(suite.T(), suite.apiWorkload). + WithMeta("some", "meta"). + Write(suite.T(), suite.client.ResourceServiceClient).Id - // Update the metadata on the apiWorkload which should result in no changes. - updatedWorkloadID := resourcetest.Resource(pbcatalog.WorkloadType, "api-abc"). - WithData(suite.T(), suite.apiWorkload). - WithMeta("some", "meta"). - Write(suite.T(), suite.client.ResourceServiceClient).Id + err := suite.ctl.Reconcile(context.Background(), suite.runtime, controller.Request{ + ID: resourceID(pbmesh.ProxyStateTemplateType, updatedWorkloadID.Name, tenancy), + }) + require.NoError(suite.T(), err) - err := suite.ctl.Reconcile(context.Background(), suite.runtime, controller.Request{ - ID: resourceID(pbmesh.ProxyStateTemplateType, updatedWorkloadID.Name), + updatedProxyState := suite.client.RequireResourceExists(suite.T(), resourceID(pbmesh.ProxyStateTemplateType, suite.apiWorkloadID.Name, tenancy)) + resourcetest.RequireVersionUnchanged(suite.T(), updatedProxyState, originalProxyState.Version) }) - require.NoError(suite.T(), err) - - updatedProxyState := suite.client.RequireResourceExists(suite.T(), resourceID(pbmesh.ProxyStateTemplateType, suite.apiWorkloadID.Name)) - resourcetest.RequireVersionUnchanged(suite.T(), updatedProxyState, originalProxyState.Version) } func (suite *controllerTestSuite) TestController() { - // This is a comprehensive test that checks the overall controller behavior as various resources change state. - // This should test interactions between the reconciler, the mappers, and the destinationsCache to ensure they work - // together and produce expected result. - - // Run the controller manager mgr := controller.NewManager(suite.client, suite.runtime.Logger) // Initialize controller dependencies. @@ -508,284 +558,302 @@ func (suite *controllerTestSuite) TestController() { mgr.SetRaftLeader(true) go mgr.Run(suite.ctx) - var ( - // Create proxy state template IDs to check against in this test. - apiProxyStateTemplateID = resourcetest.Resource(pbmesh.ProxyStateTemplateType, "api-abc").ID() - webProxyStateTemplateID = resourcetest.Resource(pbmesh.ProxyStateTemplateType, "web-def").ID() - - apiComputedRoutesID = resource.ReplaceType(pbmesh.ComputedRoutesType, suite.apiService.Id) - dbComputedRoutesID = resource.ReplaceType(pbmesh.ComputedRoutesType, suite.dbService.Id) - - apiProxyStateTemplate *pbresource.Resource - webProxyStateTemplate *pbresource.Resource - webComputedDestinations *pbresource.Resource - ) - - testutil.RunStep(suite.T(), "proxy state template generation", func(t *testing.T) { - // Check that proxy state template resource is generated for both the api and web workloads. - retry.Run(t, func(r *retry.R) { - suite.client.RequireResourceExists(r, apiProxyStateTemplateID) - webProxyStateTemplate = suite.client.RequireResourceExists(r, webProxyStateTemplateID) - apiProxyStateTemplate = suite.client.RequireResourceExists(r, apiProxyStateTemplateID) - }) - }) + suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) { + // This is a comprehensive test that checks the overall controller behavior as various resources change state. + // This should test interactions between the reconciler, the mappers, and the destinationsCache to ensure they work + // together and produce expected result. - // Write a default ComputedRoutes for api. - routestest.ReconcileComputedRoutes(suite.T(), suite.client, apiComputedRoutesID, - resourcetest.MustDecode[*pbcatalog.Service](suite.T(), suite.apiService), - ) + // Run the controller manager + var ( + // Create proxy state template IDs to check against in this test. + apiProxyStateTemplateID = resourcetest.ResourceWithTenancy(pbmesh.ProxyStateTemplateType, "api-abc", tenancy).ID() + webProxyStateTemplateID = resourcetest.ResourceWithTenancy(pbmesh.ProxyStateTemplateType, "web-def", tenancy).ID() - // Add a source service and check that a new proxy state is generated. - webComputedDestinations = resourcetest.Resource(pbmesh.ComputedExplicitDestinationsType, suite.webWorkload.Id.Name). - WithData(suite.T(), &pbmesh.ComputedExplicitDestinations{ - Destinations: []*pbmesh.Destination{ - { - DestinationRef: resource.Reference(suite.apiService.Id, ""), - DestinationPort: "tcp", - ListenAddr: &pbmesh.Destination_IpPort{ - IpPort: &pbmesh.IPPortAddress{ - Ip: "127.0.0.1", - Port: 1234, - }, - }, - }, - }, - }).Write(suite.T(), suite.client) - - testutil.RunStep(suite.T(), "add explicit destinations and check that new proxy state is generated", func(t *testing.T) { - webProxyStateTemplate = suite.client.WaitForNewVersion(suite.T(), webProxyStateTemplateID, webProxyStateTemplate.Version) + apiComputedRoutesID = resource.ReplaceType(pbmesh.ComputedRoutesType, suite.apiService.Id) + dbComputedRoutesID = resource.ReplaceType(pbmesh.ComputedRoutesType, suite.dbService.Id) - requireExplicitDestinationsFound(t, "api", webProxyStateTemplate) - }) - - testutil.RunStep(suite.T(), "update api's ports to be non-mesh", func(t *testing.T) { - // Update destination's service endpoints and workload to be non-mesh - // and check that: - // * api's proxy state template is deleted - // * we get a new web proxy resource re-generated - // * the status on Upstreams resource is updated with a validation error - nonMeshPorts := map[string]*pbcatalog.WorkloadPort{ - "tcp": {Port: 8080, Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, - } + apiProxyStateTemplate *pbresource.Resource + webProxyStateTemplate *pbresource.Resource + webComputedDestinations *pbresource.Resource + ) - // Note: the order matters here because in reality service endpoints will only - // be reconciled after the workload has been updated, and so we need to write the - // workload and service before we write service endpoints. - resourcetest.Resource(pbcatalog.WorkloadType, "api-abc"). - WithData(suite.T(), &pbcatalog.Workload{ - Identity: "api-identity", - Addresses: suite.apiWorkload.Addresses, - Ports: nonMeshPorts}). - Write(suite.T(), suite.client) + testutil.RunStep(suite.T(), "proxy state template generation", func(t *testing.T) { + // Check that proxy state template resource is generated for both the api and web workloads. + retry.Run(t, func(r *retry.R) { + suite.client.RequireResourceExists(r, apiProxyStateTemplateID) + webProxyStateTemplate = suite.client.RequireResourceExists(r, webProxyStateTemplateID) + apiProxyStateTemplate = suite.client.RequireResourceExists(r, apiProxyStateTemplateID) + }) + }) - suite.apiService = resourcetest.ResourceID(suite.apiService.Id). - WithData(t, &pbcatalog.Service{ - Workloads: &pbcatalog.WorkloadSelector{Names: []string{"api-abc"}}, - VirtualIps: []string{"1.1.1.1"}, - Ports: []*pbcatalog.ServicePort{ - {TargetPort: "tcp", Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, - // {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, - }, - }). - Write(suite.T(), suite.client) + // Write a default ComputedRoutes for api. + routestest.ReconcileComputedRoutes(suite.T(), suite.client, apiComputedRoutesID, + resourcetest.MustDecode[*pbcatalog.Service](suite.T(), suite.apiService), + ) - resourcetest.Resource(pbcatalog.ServiceEndpointsType, "api-service"). - WithData(suite.T(), &pbcatalog.ServiceEndpoints{ - Endpoints: []*pbcatalog.Endpoint{ + // Add a source service and check that a new proxy state is generated. + webComputedDestinations = resourcetest.Resource(pbmesh.ComputedExplicitDestinationsType, suite.webWorkload.Id.Name). + WithTenancy(tenancy). + WithData(suite.T(), &pbmesh.ComputedExplicitDestinations{ + Destinations: []*pbmesh.Destination{ { - TargetRef: suite.apiWorkloadID, - Addresses: suite.apiWorkload.Addresses, - Ports: nonMeshPorts, - Identity: "api-identity", + DestinationRef: resource.Reference(suite.apiService.Id, ""), + DestinationPort: "tcp", + ListenAddr: &pbmesh.Destination_IpPort{ + IpPort: &pbmesh.IPPortAddress{ + Ip: "127.0.0.1", + Port: 1234, + }, + }, }, }, - }). - Write(suite.T(), suite.client.ResourceServiceClient) + }).Write(suite.T(), suite.client) - // Refresh the computed routes in light of api losing a mesh port. - routestest.ReconcileComputedRoutes(suite.T(), suite.client, apiComputedRoutesID, - resourcetest.MustDecode[*pbcatalog.Service](t, suite.apiService), - ) + testutil.RunStep(suite.T(), "add explicit destinations and check that new proxy state is generated", func(t *testing.T) { + webProxyStateTemplate = suite.client.WaitForNewVersion(suite.T(), webProxyStateTemplateID, webProxyStateTemplate.Version) - // Check that api proxy template is gone. - retry.Run(t, func(r *retry.R) { - suite.client.RequireResourceNotFound(r, apiProxyStateTemplateID) + requireExplicitDestinationsFound(t, "api", webProxyStateTemplate) }) - // We should get a new web proxy template resource because this destination should be removed. - webProxyStateTemplate = suite.client.WaitForNewVersion(suite.T(), webProxyStateTemplateID, webProxyStateTemplate.Version) + testutil.RunStep(suite.T(), "update api's ports to be non-mesh", func(t *testing.T) { + // Update destination's service endpoints and workload to be non-mesh + // and check that: + // * api's proxy state template is deleted + // * we get a new web proxy resource re-generated + // * the status on Upstreams resource is updated with a validation error + nonMeshPorts := map[string]*pbcatalog.WorkloadPort{ + "tcp": {Port: 8080, Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, + } - requireExplicitDestinationsNotFound(t, "api", webProxyStateTemplate) - }) + // Note: the order matters here because in reality service endpoints will only + // be reconciled after the workload has been updated, and so we need to write the + // workload and service before we write service endpoints. + resourcetest.Resource(pbcatalog.WorkloadType, "api-abc"). + WithTenancy(tenancy). + WithData(suite.T(), &pbcatalog.Workload{ + Identity: "api-identity", + Addresses: suite.apiWorkload.Addresses, + Ports: nonMeshPorts}). + Write(suite.T(), suite.client) + + suite.apiService = resourcetest.ResourceID(suite.apiService.Id). + WithTenancy(tenancy). + WithData(t, &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{Names: []string{"api-abc"}}, + VirtualIps: []string{"1.1.1.1"}, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "tcp", Protocol: pbcatalog.Protocol_PROTOCOL_TCP}, + // {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + }, + }). + Write(suite.T(), suite.client) + + resourcetest.Resource(pbcatalog.ServiceEndpointsType, "api-service"). + WithTenancy(tenancy). + WithData(suite.T(), &pbcatalog.ServiceEndpoints{ + Endpoints: []*pbcatalog.Endpoint{ + { + TargetRef: suite.apiWorkloadID, + Addresses: suite.apiWorkload.Addresses, + Ports: nonMeshPorts, + Identity: "api-identity", + }, + }, + }). + Write(suite.T(), suite.client.ResourceServiceClient) - testutil.RunStep(suite.T(), "update ports to be mesh again", func(t *testing.T) { - // Update destination's service endpoints back to mesh and check that we get a new web proxy resource re-generated - // and that the status on Upstreams resource is updated to be empty. - suite.runtime.Logger.Trace("updating ports to mesh") + // Refresh the computed routes in light of api losing a mesh port. + routestest.ReconcileComputedRoutes(suite.T(), suite.client, apiComputedRoutesID, + resourcetest.MustDecode[*pbcatalog.Service](t, suite.apiService), + ) - resourcetest.Resource(pbcatalog.WorkloadType, "api-abc"). - WithData(suite.T(), suite.apiWorkload). - Write(suite.T(), suite.client) + // Check that api proxy template is gone. + retry.Run(t, func(r *retry.R) { + suite.client.RequireResourceNotFound(r, apiProxyStateTemplateID) + }) - suite.apiService = resourcetest.Resource(pbcatalog.ServiceType, "api-service"). - WithData(suite.T(), suite.apiServiceData). - Write(suite.T(), suite.client.ResourceServiceClient) + // We should get a new web proxy template resource because this destination should be removed. + webProxyStateTemplate = suite.client.WaitForNewVersion(suite.T(), webProxyStateTemplateID, webProxyStateTemplate.Version) - resourcetest.Resource(pbcatalog.ServiceEndpointsType, "api-service"). - WithData(suite.T(), suite.apiEndpointsData). - Write(suite.T(), suite.client.ResourceServiceClient) + requireExplicitDestinationsNotFound(t, "api", webProxyStateTemplate) + }) - // Refresh the computed routes in light of api losing a mesh port. - routestest.ReconcileComputedRoutes(suite.T(), suite.client, apiComputedRoutesID, - resourcetest.MustDecode[*pbcatalog.Service](t, suite.apiService), - ) + testutil.RunStep(suite.T(), "update ports to be mesh again", func(t *testing.T) { + // Update destination's service endpoints back to mesh and check that we get a new web proxy resource re-generated + // and that the status on Upstreams resource is updated to be empty. + suite.runtime.Logger.Trace("updating ports to mesh") - // We should also get a new web proxy template resource as this destination should be added again. - webProxyStateTemplate = suite.client.WaitForNewVersion(suite.T(), webProxyStateTemplateID, webProxyStateTemplate.Version) + resourcetest.Resource(pbcatalog.WorkloadType, "api-abc"). + WithTenancy(tenancy). + WithData(suite.T(), suite.apiWorkload). + Write(suite.T(), suite.client) - requireExplicitDestinationsFound(t, "api", webProxyStateTemplate) - }) + suite.apiService = resourcetest.Resource(pbcatalog.ServiceType, "api-service"). + WithData(suite.T(), suite.apiServiceData). + WithTenancy(tenancy). + Write(suite.T(), suite.client.ResourceServiceClient) - testutil.RunStep(suite.T(), "delete the proxy state template and check re-generation", func(t *testing.T) { - // Delete the proxy state template resource and check that it gets regenerated. - suite.runtime.Logger.Trace("deleting web proxy") - _, err := suite.client.Delete(suite.ctx, &pbresource.DeleteRequest{Id: webProxyStateTemplateID}) - require.NoError(suite.T(), err) + resourcetest.Resource(pbcatalog.ServiceEndpointsType, "api-service"). + WithTenancy(tenancy). + WithData(suite.T(), suite.apiEndpointsData). + Write(suite.T(), suite.client.ResourceServiceClient) - webProxyStateTemplate = suite.client.WaitForNewVersion(suite.T(), webProxyStateTemplateID, webProxyStateTemplate.Version) - requireExplicitDestinationsFound(t, "api", webProxyStateTemplate) - }) + // Refresh the computed routes in light of api losing a mesh port. + routestest.ReconcileComputedRoutes(suite.T(), suite.client, apiComputedRoutesID, + resourcetest.MustDecode[*pbcatalog.Service](t, suite.apiService), + ) - testutil.RunStep(suite.T(), "add implicit upstream and enable tproxy", func(t *testing.T) { - // Delete explicit destinations resource. - suite.runtime.Logger.Trace("deleting web destinations") - _, err := suite.client.Delete(suite.ctx, &pbresource.DeleteRequest{Id: webComputedDestinations.Id}) - require.NoError(t, err) + // We should also get a new web proxy template resource as this destination should be added again. + webProxyStateTemplate = suite.client.WaitForNewVersion(suite.T(), webProxyStateTemplateID, webProxyStateTemplate.Version) - webProxyStateTemplate = suite.client.WaitForNewVersion(suite.T(), webProxyStateTemplateID, webProxyStateTemplate.Version) + requireExplicitDestinationsFound(t, "api", webProxyStateTemplate) + }) - // Write a default ComputedRoutes for db, so it's eligible. - dbCR := routestest.ReconcileComputedRoutes(suite.T(), suite.client, dbComputedRoutesID, - resourcetest.MustDecode[*pbcatalog.Service](t, suite.dbService), - ) - require.NotNil(t, dbCR) - - // Enable transparent proxy for the web proxy. - resourcetest.Resource(pbmesh.ComputedProxyConfigurationType, suite.webWorkload.Id.Name). - WithData(t, &pbmesh.ComputedProxyConfiguration{ - DynamicConfig: &pbmesh.DynamicConfig{ - Mode: pbmesh.ProxyMode_PROXY_MODE_TRANSPARENT, - TransparentProxy: &pbmesh.TransparentProxy{ - OutboundListenerPort: 15001, + testutil.RunStep(suite.T(), "delete the proxy state template and check re-generation", func(t *testing.T) { + // Delete the proxy state template resource and check that it gets regenerated. + suite.runtime.Logger.Trace("deleting web proxy") + _, err := suite.client.Delete(suite.ctx, &pbresource.DeleteRequest{Id: webProxyStateTemplateID}) + require.NoError(suite.T(), err) + + webProxyStateTemplate = suite.client.WaitForNewVersion(suite.T(), webProxyStateTemplateID, webProxyStateTemplate.Version) + requireExplicitDestinationsFound(t, "api", webProxyStateTemplate) + }) + + testutil.RunStep(suite.T(), "add implicit upstream and enable tproxy", func(t *testing.T) { + // Delete explicit destinations resource. + suite.runtime.Logger.Trace("deleting web destinations") + _, err := suite.client.Delete(suite.ctx, &pbresource.DeleteRequest{Id: webComputedDestinations.Id}) + require.NoError(t, err) + + webProxyStateTemplate = suite.client.WaitForNewVersion(suite.T(), webProxyStateTemplateID, webProxyStateTemplate.Version) + + // Write a default ComputedRoutes for db, so it's eligible. + dbCR := routestest.ReconcileComputedRoutes(suite.T(), suite.client, dbComputedRoutesID, + resourcetest.MustDecode[*pbcatalog.Service](t, suite.dbService), + ) + require.NotNil(t, dbCR) + + // Enable transparent proxy for the web proxy. + resourcetest.Resource(pbmesh.ComputedProxyConfigurationType, suite.webWorkload.Id.Name). + WithTenancy(tenancy). + WithData(t, &pbmesh.ComputedProxyConfiguration{ + DynamicConfig: &pbmesh.DynamicConfig{ + Mode: pbmesh.ProxyMode_PROXY_MODE_TRANSPARENT, + TransparentProxy: &pbmesh.TransparentProxy{ + OutboundListenerPort: 15001, + }, }, - }, - }).Write(suite.T(), suite.client) + }).Write(suite.T(), suite.client) - webProxyStateTemplate = suite.client.WaitForNewVersion(suite.T(), webProxyStateTemplateID, webProxyStateTemplate.Version) - apiProxyStateTemplate = suite.client.WaitForNewVersion(suite.T(), apiProxyStateTemplateID, apiProxyStateTemplate.Version) + webProxyStateTemplate = suite.client.WaitForNewVersion(suite.T(), webProxyStateTemplateID, webProxyStateTemplate.Version) + apiProxyStateTemplate = suite.client.WaitForNewVersion(suite.T(), apiProxyStateTemplateID, apiProxyStateTemplate.Version) - requireImplicitDestinationsFound(t, "api", webProxyStateTemplate) - requireImplicitDestinationsFound(t, "db", webProxyStateTemplate) - }) + requireImplicitDestinationsFound(t, "api", webProxyStateTemplate) + requireImplicitDestinationsFound(t, "db", webProxyStateTemplate) + }) - testutil.RunStep(suite.T(), "traffic permissions", func(t *testing.T) { - // Global default deny applies to all identities. - assertTrafficPermissionDefaultPolicy(t, false, apiProxyStateTemplate) - assertTrafficPermissionDefaultPolicy(t, false, webProxyStateTemplate) + testutil.RunStep(suite.T(), "traffic permissions", func(t *testing.T) { + // Global default deny applies to all identities. + assertTrafficPermissionDefaultPolicy(t, false, apiProxyStateTemplate) + assertTrafficPermissionDefaultPolicy(t, false, webProxyStateTemplate) - suite.runtime.Logger.Trace("deleting computed traffic permissions") - _, err := suite.client.Delete(suite.ctx, &pbresource.DeleteRequest{Id: suite.apiComputedTrafficPermissions.Id}) - require.NoError(t, err) - suite.client.WaitForDeletion(t, suite.apiComputedTrafficPermissions.Id) + suite.runtime.Logger.Trace("deleting computed traffic permissions") + _, err := suite.client.Delete(suite.ctx, &pbresource.DeleteRequest{Id: suite.apiComputedTrafficPermissions.Id}) + require.NoError(t, err) + suite.client.WaitForDeletion(t, suite.apiComputedTrafficPermissions.Id) - apiProxyStateTemplate = suite.client.WaitForNewVersion(suite.T(), apiProxyStateTemplateID, apiProxyStateTemplate.Version) + apiProxyStateTemplate = suite.client.WaitForNewVersion(suite.T(), apiProxyStateTemplateID, apiProxyStateTemplate.Version) - suite.runtime.Logger.Trace("creating computed traffic permissions") - resourcetest.Resource(pbauth.ComputedTrafficPermissionsType, suite.apiWorkload.Identity). - WithData(t, suite.apiComputedTrafficPermissionsData). - Write(t, suite.client) + suite.runtime.Logger.Trace("creating computed traffic permissions") + resourcetest.Resource(pbauth.ComputedTrafficPermissionsType, suite.apiWorkload.Identity). + WithTenancy(tenancy). + WithData(t, suite.apiComputedTrafficPermissionsData). + Write(t, suite.client) - suite.client.WaitForNewVersion(t, apiProxyStateTemplateID, apiProxyStateTemplate.Version) - }) + suite.client.WaitForNewVersion(t, apiProxyStateTemplateID, apiProxyStateTemplate.Version) + }) - testutil.RunStep(suite.T(), "add an HTTPRoute with a simple split on the tcp port", func(t *testing.T) { - // NOTE: because at this point we have tproxy in all-to-all mode, we will get an - // implicit upstream on 'db' - - // Create a route NOT in the state store, only to more easily feed - // into the generator. - routeData := &pbmesh.HTTPRoute{ - ParentRefs: []*pbmesh.ParentReference{{ - Ref: resource.Reference(suite.dbService.Id, ""), - Port: "", // implicitly applies to 'http' - }}, - Rules: []*pbmesh.HTTPRouteRule{{ - BackendRefs: []*pbmesh.HTTPBackendRef{ - { - BackendRef: &pbmesh.BackendReference{ - Ref: resource.Reference(suite.apiService.Id, ""), - Port: "tcp", + testutil.RunStep(suite.T(), "add an HTTPRoute with a simple split on the tcp port", func(t *testing.T) { + // NOTE: because at this point we have tproxy in all-to-all mode, we will get an + // implicit upstream on 'db' + + // Create a route NOT in the state store, only to more easily feed + // into the generator. + routeData := &pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{{ + Ref: resource.Reference(suite.dbService.Id, ""), + Port: "", // implicitly applies to 'http' + }}, + Rules: []*pbmesh.HTTPRouteRule{{ + BackendRefs: []*pbmesh.HTTPBackendRef{ + { + BackendRef: &pbmesh.BackendReference{ + Ref: resource.Reference(suite.apiService.Id, ""), + Port: "tcp", + }, + Weight: 60, }, - Weight: 60, - }, - { - BackendRef: &pbmesh.BackendReference{ - Ref: resource.Reference(suite.dbService.Id, ""), - Port: "", // assumed to be 'http' + { + BackendRef: &pbmesh.BackendReference{ + Ref: resource.Reference(suite.dbService.Id, ""), + Port: "", // assumed to be 'http' + }, + Weight: 40, }, - Weight: 40, }, - }, - }}, - } - route := resourcetest.Resource(pbmesh.HTTPRouteType, "db-http-route"). - WithTenancy(resource.DefaultNamespacedTenancy()). - WithData(t, routeData). - Build() - require.NoError(t, types.MutateHTTPRoute(route)) - require.NoError(t, types.ValidateHTTPRoute(route)) - - dbCRID := resource.ReplaceType(pbmesh.ComputedRoutesType, suite.dbService.Id) - - dbCR := routestest.ReconcileComputedRoutes(suite.T(), suite.client, dbCRID, - resourcetest.MustDecode[*pbmesh.HTTPRoute](t, route), - resourcetest.MustDecode[*pbcatalog.Service](t, suite.dbService), - resourcetest.MustDecode[*pbcatalog.Service](t, suite.apiService), - ) - require.NotNil(t, dbCR, "computed routes for db was deleted instead of created") - - webProxyStateTemplate = suite.client.WaitForNewVersion(suite.T(), webProxyStateTemplateID, webProxyStateTemplate.Version) - - requireImplicitDestinationsFound(t, "api", webProxyStateTemplate) - requireImplicitDestinationsFound(t, "db", webProxyStateTemplate) + }}, + } + route := resourcetest.Resource(pbmesh.HTTPRouteType, "db-http-route"). + WithTenancy(tenancy). + WithData(t, routeData). + Build() + require.NoError(t, types.MutateHTTPRoute(route)) + require.NoError(t, types.ValidateHTTPRoute(route)) + + dbCRID := resource.ReplaceType(pbmesh.ComputedRoutesType, suite.dbService.Id) + + dbCR := routestest.ReconcileComputedRoutes(suite.T(), suite.client, dbCRID, + resourcetest.MustDecode[*pbmesh.HTTPRoute](t, route), + resourcetest.MustDecode[*pbcatalog.Service](t, suite.dbService), + resourcetest.MustDecode[*pbcatalog.Service](t, suite.apiService), + ) + require.NotNil(t, dbCR, "computed routes for db was deleted instead of created") + + webProxyStateTemplate = suite.client.WaitForNewVersion(suite.T(), webProxyStateTemplateID, webProxyStateTemplate.Version) + + requireImplicitDestinationsFound(t, "api", webProxyStateTemplate) + requireImplicitDestinationsFound(t, "db", webProxyStateTemplate) + }) }) } func (suite *controllerTestSuite) TestControllerDefaultAllow() { - // Run the controller manager - mgr := controller.NewManager(suite.client, suite.runtime.Logger) - - // Initialize controller dependencies. - c := cache.New() - trustDomainFetcher := func() (string, error) { return "test.consul", nil } - - mgr.Register(Controller(c, trustDomainFetcher, "dc1", true)) - mgr.SetRaftLeader(true) - go mgr.Run(suite.ctx) - - var ( - // Create proxy state template IDs to check against in this test. - apiProxyStateTemplateID = resourcetest.Resource(pbmesh.ProxyStateTemplateType, "api-abc").ID() - webProxyStateTemplateID = resourcetest.Resource(pbmesh.ProxyStateTemplateType, "web-def").ID() - ) + suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) { + // Run the controller manager + mgr := controller.NewManager(suite.client, suite.runtime.Logger) + + // Initialize controller dependencies. + c := cache.New() + trustDomainFetcher := func() (string, error) { return "test.consul", nil } + + mgr.Register(Controller(c, trustDomainFetcher, "dc1", true)) + mgr.SetRaftLeader(true) + go mgr.Run(suite.ctx) + + var ( + // Create proxy state template IDs to check against in this test. + apiProxyStateTemplateID = resourcetest.Resource(pbmesh.ProxyStateTemplateType, "api-abc").WithTenancy(tenancy).ID() + webProxyStateTemplateID = resourcetest.Resource(pbmesh.ProxyStateTemplateType, "web-def").WithTenancy(tenancy).ID() + ) - retry.Run(suite.T(), func(r *retry.R) { - webProxyStateTemplate := suite.client.RequireResourceExists(r, webProxyStateTemplateID) - apiProxyStateTemplate := suite.client.RequireResourceExists(r, apiProxyStateTemplateID) + retry.Run(suite.T(), func(r *retry.R) { + webProxyStateTemplate := suite.client.RequireResourceExists(r, webProxyStateTemplateID) + apiProxyStateTemplate := suite.client.RequireResourceExists(r, apiProxyStateTemplateID) - // Default deny because api has non-empty computed traffic permissions. - assertTrafficPermissionDefaultPolicy(r, false, apiProxyStateTemplate) - assertTrafficPermissionDefaultPolicy(r, true, webProxyStateTemplate) + // Default deny because api has non-empty computed traffic permissions. + assertTrafficPermissionDefaultPolicy(r, false, apiProxyStateTemplate) + assertTrafficPermissionDefaultPolicy(r, true, webProxyStateTemplate) + }) }) } @@ -893,15 +961,11 @@ func requireClustersAndEndpoints(t *testing.T, name string, tmpl *pbmesh.ProxySt require.Equal(t, found, foundEndpoints) } -func resourceID(rtype *pbresource.Type, name string) *pbresource.ID { +func resourceID(rtype *pbresource.Type, name string, tenancy *pbresource.Tenancy) *pbresource.ID { return &pbresource.ID{ - Type: rtype, - Tenancy: &pbresource.Tenancy{ - Partition: "default", - Namespace: "default", - PeerName: "local", - }, - Name: name, + Type: rtype, + Tenancy: tenancy, + Name: name, } } @@ -919,3 +983,31 @@ func assertTrafficPermissionDefaultPolicy(t resourcetest.T, defaultAllow bool, r require.NotNil(t, l4) require.Equal(t, defaultAllow, l4.TrafficPermissions.DefaultAllow) } + +func (suite *controllerTestSuite) appendTenancyInfo(tenancy *pbresource.Tenancy) string { + return fmt.Sprintf("%s_Namespace_%s_Partition", tenancy.Namespace, tenancy.Partition) +} + +func (suite *controllerTestSuite) cleanupResources() { + + suite.client.MustDelete(suite.T(), suite.apiWorkloadID) + suite.client.MustDelete(suite.T(), suite.apiComputedTrafficPermissions.Id) + suite.client.MustDelete(suite.T(), suite.apiService.Id) + suite.client.MustDelete(suite.T(), suite.apiEndpoints.Id) + suite.client.MustDelete(suite.T(), suite.webWorkload.Id) + suite.client.MustDelete(suite.T(), suite.dbWorkloadID) + suite.client.MustDelete(suite.T(), suite.dbService.Id) + suite.client.MustDelete(suite.T(), suite.dbEndpoints.Id) +} + +func (suite *controllerTestSuite) runTestCaseWithTenancies(t func(*pbresource.Tenancy)) { + for _, tenancy := range suite.tenancies { + suite.Run(suite.appendTenancyInfo(tenancy), func() { + suite.setupSuiteWithTenancy(tenancy) + suite.T().Cleanup(func() { + suite.cleanupResources() + }) + t(tenancy) + }) + } +} diff --git a/internal/resource/resourcetest/builder.go b/internal/resource/resourcetest/builder.go index 423bd66aaa22..0379a4765937 100644 --- a/internal/resource/resourcetest/builder.go +++ b/internal/resource/resourcetest/builder.go @@ -43,6 +43,22 @@ func Resource(rtype *pbresource.Type, name string) *resourceBuilder { } } +func ResourceWithTenancy(rtype *pbresource.Type, name string, tenancy *pbresource.Tenancy) *resourceBuilder { + return &resourceBuilder{ + resource: &pbresource.Resource{ + Id: &pbresource.ID{ + Type: &pbresource.Type{ + Group: rtype.Group, + GroupVersion: rtype.GroupVersion, + Kind: rtype.Kind, + }, + Name: name, + Tenancy: tenancy, + }, + }, + } +} + func ResourceID(id *pbresource.ID) *resourceBuilder { return &resourceBuilder{ resource: &pbresource.Resource{