|
|
|
@ -5,6 +5,7 @@ package endpoints
|
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
|
"context" |
|
|
|
|
"fmt" |
|
|
|
|
"testing" |
|
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/require" |
|
|
|
@ -15,6 +16,7 @@ import (
|
|
|
|
|
"github.com/hashicorp/consul/internal/catalog/internal/types" |
|
|
|
|
"github.com/hashicorp/consul/internal/controller" |
|
|
|
|
"github.com/hashicorp/consul/internal/resource/mappers/selectiontracker" |
|
|
|
|
"github.com/hashicorp/consul/internal/resource/resourcetest" |
|
|
|
|
rtest "github.com/hashicorp/consul/internal/resource/resourcetest" |
|
|
|
|
pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v2beta1" |
|
|
|
|
"github.com/hashicorp/consul/proto-public/pbresource" |
|
|
|
@ -441,11 +443,13 @@ type controllerSuite struct {
|
|
|
|
|
|
|
|
|
|
tracker *selectiontracker.WorkloadSelectionTracker |
|
|
|
|
reconciler *serviceEndpointsReconciler |
|
|
|
|
tenancies []*pbresource.Tenancy |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (suite *controllerSuite) SetupTest() { |
|
|
|
|
suite.tenancies = resourcetest.TestTenancies() |
|
|
|
|
suite.ctx = testutil.TestContext(suite.T()) |
|
|
|
|
client := svctest.RunResourceService(suite.T(), types.Register) |
|
|
|
|
client := svctest.RunResourceServiceWithTenancies(suite.T(), types.Register) |
|
|
|
|
suite.rt = controller.Runtime{ |
|
|
|
|
Client: client, |
|
|
|
|
Logger: testutil.Logger(suite.T()), |
|
|
|
@ -478,25 +482,28 @@ func (suite *controllerSuite) TestReconcile_ServiceNotFound() {
|
|
|
|
|
|
|
|
|
|
// generate a workload resource to use for checking if it maps
|
|
|
|
|
// to a service endpoints object
|
|
|
|
|
workload := rtest.Resource(pbcatalog.WorkloadType, "foo").Build() |
|
|
|
|
|
|
|
|
|
// ensure that the tracker knows about the service prior to
|
|
|
|
|
// calling reconcile so that we can ensure it removes tracking
|
|
|
|
|
id := rtest.Resource(pbcatalog.ServiceEndpointsType, "not-found").ID() |
|
|
|
|
suite.tracker.TrackIDForSelector(id, &pbcatalog.WorkloadSelector{Prefixes: []string{""}}) |
|
|
|
|
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) { |
|
|
|
|
workload := rtest.Resource(pbcatalog.WorkloadType, "foo").WithTenancy(tenancy).Build() |
|
|
|
|
|
|
|
|
|
// verify that mapping the workload to service endpoints returns a
|
|
|
|
|
// non-empty list prior to reconciliation which should remove the
|
|
|
|
|
// tracking.
|
|
|
|
|
suite.requireTracking(workload, id) |
|
|
|
|
// ensure that the tracker knows about the service prior to
|
|
|
|
|
// calling reconcile so that we can ensure it removes tracking
|
|
|
|
|
id := rtest.Resource(pbcatalog.ServiceEndpointsType, "not-found").WithTenancy(tenancy).ID() |
|
|
|
|
suite.tracker.TrackIDForSelector(id, &pbcatalog.WorkloadSelector{Prefixes: []string{""}}) |
|
|
|
|
|
|
|
|
|
// Because the endpoints don't exist, this reconcile call should
|
|
|
|
|
// cause tracking of the endpoints to be removed
|
|
|
|
|
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id}) |
|
|
|
|
require.NoError(suite.T(), err) |
|
|
|
|
// verify that mapping the workload to service endpoints returns a
|
|
|
|
|
// non-empty list prior to reconciliation which should remove the
|
|
|
|
|
// tracking.
|
|
|
|
|
suite.requireTracking(workload, id) |
|
|
|
|
|
|
|
|
|
// Because the endpoints don't exist, this reconcile call should
|
|
|
|
|
// cause tracking of the endpoints to be removed
|
|
|
|
|
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id}) |
|
|
|
|
require.NoError(suite.T(), err) |
|
|
|
|
|
|
|
|
|
// Now ensure that the tracking was removed
|
|
|
|
|
suite.requireTracking(workload) |
|
|
|
|
// Now ensure that the tracking was removed
|
|
|
|
|
suite.requireTracking(workload) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (suite *controllerSuite) TestReconcile_NoSelector_NoEndpoints() { |
|
|
|
@ -505,20 +512,23 @@ func (suite *controllerSuite) TestReconcile_NoSelector_NoEndpoints() {
|
|
|
|
|
// managed. Additionally, with no endpoints pre-existing it will
|
|
|
|
|
// not attempt to delete them.
|
|
|
|
|
|
|
|
|
|
service := rtest.Resource(pbcatalog.ServiceType, "test"). |
|
|
|
|
WithData(suite.T(), &pbcatalog.Service{ |
|
|
|
|
Ports: []*pbcatalog.ServicePort{ |
|
|
|
|
{TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, |
|
|
|
|
}, |
|
|
|
|
}). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) { |
|
|
|
|
service := rtest.Resource(pbcatalog.ServiceType, "test"). |
|
|
|
|
WithTenancy(tenancy). |
|
|
|
|
WithData(suite.T(), &pbcatalog.Service{ |
|
|
|
|
Ports: []*pbcatalog.ServicePort{ |
|
|
|
|
{TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, |
|
|
|
|
}, |
|
|
|
|
}). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
|
|
|
|
|
endpointsID := rtest.Resource(pbcatalog.ServiceEndpointsType, "test").ID() |
|
|
|
|
endpointsID := rtest.Resource(pbcatalog.ServiceEndpointsType, "test").WithTenancy(tenancy).ID() |
|
|
|
|
|
|
|
|
|
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: endpointsID}) |
|
|
|
|
require.NoError(suite.T(), err) |
|
|
|
|
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: endpointsID}) |
|
|
|
|
require.NoError(suite.T(), err) |
|
|
|
|
|
|
|
|
|
suite.client.RequireStatusCondition(suite.T(), service.Id, StatusKey, ConditionUnmanaged) |
|
|
|
|
suite.client.RequireStatusCondition(suite.T(), service.Id, StatusKey, ConditionUnmanaged) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (suite *controllerSuite) TestReconcile_NoSelector_ManagedEndpoints() { |
|
|
|
@ -526,26 +536,30 @@ func (suite *controllerSuite) TestReconcile_NoSelector_ManagedEndpoints() {
|
|
|
|
|
// to unmanaged endpoints for a service, any already generated managed endpoints
|
|
|
|
|
// get deleted.
|
|
|
|
|
|
|
|
|
|
service := rtest.Resource(pbcatalog.ServiceType, "test"). |
|
|
|
|
WithData(suite.T(), &pbcatalog.Service{ |
|
|
|
|
Ports: []*pbcatalog.ServicePort{ |
|
|
|
|
{TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, |
|
|
|
|
}, |
|
|
|
|
}). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
|
|
|
|
|
endpoints := rtest.Resource(pbcatalog.ServiceEndpointsType, "test"). |
|
|
|
|
WithData(suite.T(), &pbcatalog.ServiceEndpoints{}). |
|
|
|
|
// this marks these endpoints as under management
|
|
|
|
|
WithMeta(endpointsMetaManagedBy, StatusKey). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
|
|
|
|
|
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: endpoints.Id}) |
|
|
|
|
require.NoError(suite.T(), err) |
|
|
|
|
// the status should indicate the services endpoints are not being managed
|
|
|
|
|
suite.client.RequireStatusCondition(suite.T(), service.Id, StatusKey, ConditionUnmanaged) |
|
|
|
|
// endpoints under management should be deleted
|
|
|
|
|
suite.client.RequireResourceNotFound(suite.T(), endpoints.Id) |
|
|
|
|
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) { |
|
|
|
|
service := rtest.Resource(pbcatalog.ServiceType, "test"). |
|
|
|
|
WithTenancy(tenancy). |
|
|
|
|
WithData(suite.T(), &pbcatalog.Service{ |
|
|
|
|
Ports: []*pbcatalog.ServicePort{ |
|
|
|
|
{TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, |
|
|
|
|
}, |
|
|
|
|
}). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
|
|
|
|
|
endpoints := rtest.Resource(pbcatalog.ServiceEndpointsType, "test"). |
|
|
|
|
WithTenancy(tenancy). |
|
|
|
|
WithData(suite.T(), &pbcatalog.ServiceEndpoints{}). |
|
|
|
|
// this marks these endpoints as under management
|
|
|
|
|
WithMeta(endpointsMetaManagedBy, StatusKey). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
|
|
|
|
|
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: endpoints.Id}) |
|
|
|
|
require.NoError(suite.T(), err) |
|
|
|
|
// the status should indicate the services endpoints are not being managed
|
|
|
|
|
suite.client.RequireStatusCondition(suite.T(), service.Id, StatusKey, ConditionUnmanaged) |
|
|
|
|
// endpoints under management should be deleted
|
|
|
|
|
suite.client.RequireResourceNotFound(suite.T(), endpoints.Id) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (suite *controllerSuite) TestReconcile_NoSelector_UnmanagedEndpoints() { |
|
|
|
@ -553,65 +567,73 @@ func (suite *controllerSuite) TestReconcile_NoSelector_UnmanagedEndpoints() {
|
|
|
|
|
// doesn't have its endpoints managed, that we do not delete any unmanaged
|
|
|
|
|
// ServiceEndpoints resource that the user would have manually written.
|
|
|
|
|
|
|
|
|
|
service := rtest.Resource(pbcatalog.ServiceType, "test"). |
|
|
|
|
WithData(suite.T(), &pbcatalog.Service{ |
|
|
|
|
Ports: []*pbcatalog.ServicePort{ |
|
|
|
|
{TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, |
|
|
|
|
}, |
|
|
|
|
}). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
|
|
|
|
|
endpoints := rtest.Resource(pbcatalog.ServiceEndpointsType, "test"). |
|
|
|
|
WithData(suite.T(), &pbcatalog.ServiceEndpoints{}). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
|
|
|
|
|
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: endpoints.Id}) |
|
|
|
|
require.NoError(suite.T(), err) |
|
|
|
|
// the status should indicate the services endpoints are not being managed
|
|
|
|
|
suite.client.RequireStatusCondition(suite.T(), service.Id, StatusKey, ConditionUnmanaged) |
|
|
|
|
// unmanaged endpoints should not be deleted when the service is unmanaged
|
|
|
|
|
suite.client.RequireResourceExists(suite.T(), endpoints.Id) |
|
|
|
|
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) { |
|
|
|
|
service := rtest.Resource(pbcatalog.ServiceType, "test"). |
|
|
|
|
WithTenancy(tenancy). |
|
|
|
|
WithData(suite.T(), &pbcatalog.Service{ |
|
|
|
|
Ports: []*pbcatalog.ServicePort{ |
|
|
|
|
{TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, |
|
|
|
|
}, |
|
|
|
|
}). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
|
|
|
|
|
endpoints := rtest.Resource(pbcatalog.ServiceEndpointsType, "test"). |
|
|
|
|
WithTenancy(tenancy). |
|
|
|
|
WithData(suite.T(), &pbcatalog.ServiceEndpoints{}). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
|
|
|
|
|
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: endpoints.Id}) |
|
|
|
|
require.NoError(suite.T(), err) |
|
|
|
|
// the status should indicate the services endpoints are not being managed
|
|
|
|
|
suite.client.RequireStatusCondition(suite.T(), service.Id, StatusKey, ConditionUnmanaged) |
|
|
|
|
// unmanaged endpoints should not be deleted when the service is unmanaged
|
|
|
|
|
suite.client.RequireResourceExists(suite.T(), endpoints.Id) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (suite *controllerSuite) TestReconcile_Managed_NoPreviousEndpoints() { |
|
|
|
|
// This test's purpose is to ensure the managed endpoint generation occurs
|
|
|
|
|
// as expected when there are no pre-existing endpoints.
|
|
|
|
|
|
|
|
|
|
service := rtest.Resource(pbcatalog.ServiceType, "test"). |
|
|
|
|
WithData(suite.T(), &pbcatalog.Service{ |
|
|
|
|
Workloads: &pbcatalog.WorkloadSelector{ |
|
|
|
|
Prefixes: []string{""}, |
|
|
|
|
}, |
|
|
|
|
Ports: []*pbcatalog.ServicePort{ |
|
|
|
|
{TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, |
|
|
|
|
}, |
|
|
|
|
}). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) { |
|
|
|
|
service := rtest.Resource(pbcatalog.ServiceType, "test"). |
|
|
|
|
WithTenancy(tenancy). |
|
|
|
|
WithData(suite.T(), &pbcatalog.Service{ |
|
|
|
|
Workloads: &pbcatalog.WorkloadSelector{ |
|
|
|
|
Prefixes: []string{""}, |
|
|
|
|
}, |
|
|
|
|
Ports: []*pbcatalog.ServicePort{ |
|
|
|
|
{TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, |
|
|
|
|
}, |
|
|
|
|
}). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
|
|
|
|
|
endpointsID := rtest.Resource(pbcatalog.ServiceEndpointsType, "test").ID() |
|
|
|
|
endpointsID := rtest.Resource(pbcatalog.ServiceEndpointsType, "test").WithTenancy(tenancy).ID() |
|
|
|
|
|
|
|
|
|
rtest.Resource(pbcatalog.WorkloadType, "test-workload"). |
|
|
|
|
WithData(suite.T(), &pbcatalog.Workload{ |
|
|
|
|
Addresses: []*pbcatalog.WorkloadAddress{{Host: "127.0.0.1"}}, |
|
|
|
|
Ports: map[string]*pbcatalog.WorkloadPort{ |
|
|
|
|
"http": {Port: 8080, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, |
|
|
|
|
}, |
|
|
|
|
}). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
rtest.Resource(pbcatalog.WorkloadType, "test-workload"). |
|
|
|
|
WithTenancy(tenancy). |
|
|
|
|
WithData(suite.T(), &pbcatalog.Workload{ |
|
|
|
|
Addresses: []*pbcatalog.WorkloadAddress{{Host: "127.0.0.1"}}, |
|
|
|
|
Ports: map[string]*pbcatalog.WorkloadPort{ |
|
|
|
|
"http": {Port: 8080, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, |
|
|
|
|
}, |
|
|
|
|
}). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
|
|
|
|
|
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: endpointsID}) |
|
|
|
|
require.NoError(suite.T(), err) |
|
|
|
|
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: endpointsID}) |
|
|
|
|
require.NoError(suite.T(), err) |
|
|
|
|
|
|
|
|
|
// Verify that the services status has been set to indicate endpoints are automatically managed.
|
|
|
|
|
suite.client.RequireStatusCondition(suite.T(), service.Id, StatusKey, ConditionManaged) |
|
|
|
|
// Verify that the services status has been set to indicate endpoints are automatically managed.
|
|
|
|
|
suite.client.RequireStatusCondition(suite.T(), service.Id, StatusKey, ConditionManaged) |
|
|
|
|
|
|
|
|
|
// The service endpoints metadata should include our tag to indcate it was generated by this controller
|
|
|
|
|
res := suite.client.RequireResourceMeta(suite.T(), endpointsID, endpointsMetaManagedBy, StatusKey) |
|
|
|
|
// The service endpoints metadata should include our tag to indcate it was generated by this controller
|
|
|
|
|
res := suite.client.RequireResourceMeta(suite.T(), endpointsID, endpointsMetaManagedBy, StatusKey) |
|
|
|
|
|
|
|
|
|
var endpoints pbcatalog.ServiceEndpoints |
|
|
|
|
err = res.Data.UnmarshalTo(&endpoints) |
|
|
|
|
require.NoError(suite.T(), err) |
|
|
|
|
require.Len(suite.T(), endpoints.Endpoints, 1) |
|
|
|
|
var endpoints pbcatalog.ServiceEndpoints |
|
|
|
|
err = res.Data.UnmarshalTo(&endpoints) |
|
|
|
|
require.NoError(suite.T(), err) |
|
|
|
|
require.Len(suite.T(), endpoints.Endpoints, 1) |
|
|
|
|
}) |
|
|
|
|
// We are not going to retest that the workloads to endpoints conversion process
|
|
|
|
|
// The length check should be sufficient to prove the endpoints are being
|
|
|
|
|
// converted. The unit tests for the workloadsToEndpoints functions prove that
|
|
|
|
@ -622,41 +644,46 @@ func (suite *controllerSuite) TestReconcile_Managed_ExistingEndpoints() {
|
|
|
|
|
// This test's purpose is to ensure that when the current set of endpoints
|
|
|
|
|
// differs from any prior set of endpoints that the resource gets rewritten.
|
|
|
|
|
|
|
|
|
|
service := rtest.Resource(pbcatalog.ServiceType, "test"). |
|
|
|
|
WithData(suite.T(), &pbcatalog.Service{ |
|
|
|
|
Workloads: &pbcatalog.WorkloadSelector{ |
|
|
|
|
Prefixes: []string{""}, |
|
|
|
|
}, |
|
|
|
|
Ports: []*pbcatalog.ServicePort{ |
|
|
|
|
{TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, |
|
|
|
|
}, |
|
|
|
|
}). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
|
|
|
|
|
endpoints := rtest.Resource(pbcatalog.ServiceEndpointsType, "test"). |
|
|
|
|
WithData(suite.T(), &pbcatalog.ServiceEndpoints{}). |
|
|
|
|
WithOwner(service.Id). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
|
|
|
|
|
rtest.Resource(pbcatalog.WorkloadType, "test-workload"). |
|
|
|
|
WithData(suite.T(), &pbcatalog.Workload{ |
|
|
|
|
Addresses: []*pbcatalog.WorkloadAddress{{Host: "127.0.0.1"}}, |
|
|
|
|
Ports: map[string]*pbcatalog.WorkloadPort{ |
|
|
|
|
"http": {Port: 8080, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, |
|
|
|
|
}, |
|
|
|
|
}). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) { |
|
|
|
|
service := rtest.Resource(pbcatalog.ServiceType, "test"). |
|
|
|
|
WithTenancy(tenancy). |
|
|
|
|
WithData(suite.T(), &pbcatalog.Service{ |
|
|
|
|
Workloads: &pbcatalog.WorkloadSelector{ |
|
|
|
|
Prefixes: []string{""}, |
|
|
|
|
}, |
|
|
|
|
Ports: []*pbcatalog.ServicePort{ |
|
|
|
|
{TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, |
|
|
|
|
}, |
|
|
|
|
}). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
|
|
|
|
|
endpoints := rtest.Resource(pbcatalog.ServiceEndpointsType, "test"). |
|
|
|
|
WithTenancy(tenancy). |
|
|
|
|
WithData(suite.T(), &pbcatalog.ServiceEndpoints{}). |
|
|
|
|
WithOwner(service.Id). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
|
|
|
|
|
rtest.Resource(pbcatalog.WorkloadType, "test-workload"). |
|
|
|
|
WithTenancy(tenancy). |
|
|
|
|
WithData(suite.T(), &pbcatalog.Workload{ |
|
|
|
|
Addresses: []*pbcatalog.WorkloadAddress{{Host: "127.0.0.1"}}, |
|
|
|
|
Ports: map[string]*pbcatalog.WorkloadPort{ |
|
|
|
|
"http": {Port: 8080, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, |
|
|
|
|
}, |
|
|
|
|
}). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
|
|
|
|
|
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: endpoints.Id}) |
|
|
|
|
require.NoError(suite.T(), err) |
|
|
|
|
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: endpoints.Id}) |
|
|
|
|
require.NoError(suite.T(), err) |
|
|
|
|
|
|
|
|
|
suite.client.RequireStatusCondition(suite.T(), service.Id, StatusKey, ConditionManaged) |
|
|
|
|
res := suite.client.RequireResourceMeta(suite.T(), endpoints.Id, endpointsMetaManagedBy, StatusKey) |
|
|
|
|
suite.client.RequireStatusCondition(suite.T(), service.Id, StatusKey, ConditionManaged) |
|
|
|
|
res := suite.client.RequireResourceMeta(suite.T(), endpoints.Id, endpointsMetaManagedBy, StatusKey) |
|
|
|
|
|
|
|
|
|
var newEndpoints pbcatalog.ServiceEndpoints |
|
|
|
|
err = res.Data.UnmarshalTo(&newEndpoints) |
|
|
|
|
require.NoError(suite.T(), err) |
|
|
|
|
require.Len(suite.T(), newEndpoints.Endpoints, 1) |
|
|
|
|
var newEndpoints pbcatalog.ServiceEndpoints |
|
|
|
|
err = res.Data.UnmarshalTo(&newEndpoints) |
|
|
|
|
require.NoError(suite.T(), err) |
|
|
|
|
require.Len(suite.T(), newEndpoints.Endpoints, 1) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (suite *controllerSuite) TestController() { |
|
|
|
@ -673,184 +700,202 @@ func (suite *controllerSuite) TestController() {
|
|
|
|
|
mgr.SetRaftLeader(true) |
|
|
|
|
go mgr.Run(suite.ctx) |
|
|
|
|
|
|
|
|
|
// Add a service - there are no workloads so an empty endpoints
|
|
|
|
|
// object should be created.
|
|
|
|
|
service := rtest.Resource(pbcatalog.ServiceType, "api"). |
|
|
|
|
WithData(suite.T(), &pbcatalog.Service{ |
|
|
|
|
Workloads: &pbcatalog.WorkloadSelector{ |
|
|
|
|
Prefixes: []string{"api-"}, |
|
|
|
|
}, |
|
|
|
|
Ports: []*pbcatalog.ServicePort{ |
|
|
|
|
{TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, |
|
|
|
|
suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) { |
|
|
|
|
// Add a service - there are no workloads so an empty endpoints
|
|
|
|
|
// object should be created.
|
|
|
|
|
service := rtest.Resource(pbcatalog.ServiceType, "api"). |
|
|
|
|
WithTenancy(tenancy). |
|
|
|
|
WithData(suite.T(), &pbcatalog.Service{ |
|
|
|
|
Workloads: &pbcatalog.WorkloadSelector{ |
|
|
|
|
Prefixes: []string{"api-"}, |
|
|
|
|
}, |
|
|
|
|
Ports: []*pbcatalog.ServicePort{ |
|
|
|
|
{TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, |
|
|
|
|
}, |
|
|
|
|
}). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
|
|
|
|
|
// Wait for the controller to record that the endpoints are being managed
|
|
|
|
|
res := suite.client.WaitForReconciliation(suite.T(), service.Id, StatusKey) |
|
|
|
|
// Check that the services status was updated accordingly
|
|
|
|
|
rtest.RequireStatusCondition(suite.T(), res, StatusKey, ConditionManaged) |
|
|
|
|
rtest.RequireStatusCondition(suite.T(), res, StatusKey, ConditionIdentitiesNotFound) |
|
|
|
|
|
|
|
|
|
// Check that the endpoints resource exists and contains 0 endpoints
|
|
|
|
|
endpointsID := rtest.Resource(pbcatalog.ServiceEndpointsType, "api").WithTenancy(tenancy).ID() |
|
|
|
|
endpoints := suite.client.RequireResourceExists(suite.T(), endpointsID) |
|
|
|
|
suite.requireEndpoints(endpoints) |
|
|
|
|
|
|
|
|
|
// Now add a workload that would be selected by the service. Leave
|
|
|
|
|
// the workload in a state where its health has not been reconciled
|
|
|
|
|
workload := rtest.Resource(pbcatalog.WorkloadType, "api-1"). |
|
|
|
|
WithTenancy(tenancy). |
|
|
|
|
WithData(suite.T(), &pbcatalog.Workload{ |
|
|
|
|
Addresses: []*pbcatalog.WorkloadAddress{{Host: "127.0.0.1"}}, |
|
|
|
|
Ports: map[string]*pbcatalog.WorkloadPort{ |
|
|
|
|
"http": {Port: 8080, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, |
|
|
|
|
"grpc": {Port: 8081, Protocol: pbcatalog.Protocol_PROTOCOL_GRPC}, |
|
|
|
|
}, |
|
|
|
|
Identity: "api", |
|
|
|
|
}). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
|
|
|
|
|
suite.client.WaitForStatusCondition(suite.T(), service.Id, StatusKey, |
|
|
|
|
ConditionIdentitiesFound([]string{"api"})) |
|
|
|
|
|
|
|
|
|
// Wait for the endpoints to be regenerated
|
|
|
|
|
endpoints = suite.client.WaitForNewVersion(suite.T(), endpointsID, endpoints.Version) |
|
|
|
|
|
|
|
|
|
// Verify that the generated endpoints now contain the workload
|
|
|
|
|
suite.requireEndpoints(endpoints, &pbcatalog.Endpoint{ |
|
|
|
|
TargetRef: workload.Id, |
|
|
|
|
Addresses: []*pbcatalog.WorkloadAddress{ |
|
|
|
|
{Host: "127.0.0.1", Ports: []string{"http"}}, |
|
|
|
|
}, |
|
|
|
|
}). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
|
|
|
|
|
// Wait for the controller to record that the endpoints are being managed
|
|
|
|
|
res := suite.client.WaitForReconciliation(suite.T(), service.Id, StatusKey) |
|
|
|
|
// Check that the services status was updated accordingly
|
|
|
|
|
rtest.RequireStatusCondition(suite.T(), res, StatusKey, ConditionManaged) |
|
|
|
|
rtest.RequireStatusCondition(suite.T(), res, StatusKey, ConditionIdentitiesNotFound) |
|
|
|
|
|
|
|
|
|
// Check that the endpoints resource exists and contains 0 endpoints
|
|
|
|
|
endpointsID := rtest.Resource(pbcatalog.ServiceEndpointsType, "api").ID() |
|
|
|
|
endpoints := suite.client.RequireResourceExists(suite.T(), endpointsID) |
|
|
|
|
suite.requireEndpoints(endpoints) |
|
|
|
|
|
|
|
|
|
// Now add a workload that would be selected by the service. Leave
|
|
|
|
|
// the workload in a state where its health has not been reconciled
|
|
|
|
|
workload := rtest.Resource(pbcatalog.WorkloadType, "api-1"). |
|
|
|
|
WithData(suite.T(), &pbcatalog.Workload{ |
|
|
|
|
Addresses: []*pbcatalog.WorkloadAddress{{Host: "127.0.0.1"}}, |
|
|
|
|
Ports: map[string]*pbcatalog.WorkloadPort{ |
|
|
|
|
"http": {Port: 8080, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, |
|
|
|
|
"grpc": {Port: 8081, Protocol: pbcatalog.Protocol_PROTOCOL_GRPC}, |
|
|
|
|
}, |
|
|
|
|
Identity: "api", |
|
|
|
|
}). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
|
|
|
|
|
suite.client.WaitForStatusCondition(suite.T(), service.Id, StatusKey, |
|
|
|
|
ConditionIdentitiesFound([]string{"api"})) |
|
|
|
|
|
|
|
|
|
// Wait for the endpoints to be regenerated
|
|
|
|
|
endpoints = suite.client.WaitForNewVersion(suite.T(), endpointsID, endpoints.Version) |
|
|
|
|
|
|
|
|
|
// Verify that the generated endpoints now contain the workload
|
|
|
|
|
suite.requireEndpoints(endpoints, &pbcatalog.Endpoint{ |
|
|
|
|
TargetRef: workload.Id, |
|
|
|
|
Addresses: []*pbcatalog.WorkloadAddress{ |
|
|
|
|
{Host: "127.0.0.1", Ports: []string{"http"}}, |
|
|
|
|
}, |
|
|
|
|
Ports: map[string]*pbcatalog.WorkloadPort{ |
|
|
|
|
"http": {Port: 8080, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, |
|
|
|
|
}, |
|
|
|
|
HealthStatus: pbcatalog.Health_HEALTH_CRITICAL, |
|
|
|
|
Identity: "api", |
|
|
|
|
}) |
|
|
|
|
HealthStatus: pbcatalog.Health_HEALTH_CRITICAL, |
|
|
|
|
Identity: "api", |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
// Update the health status of the workload
|
|
|
|
|
suite.client.WriteStatus(suite.ctx, &pbresource.WriteStatusRequest{ |
|
|
|
|
Id: workload.Id, |
|
|
|
|
Key: workloadhealth.StatusKey, |
|
|
|
|
Status: &pbresource.Status{ |
|
|
|
|
ObservedGeneration: workload.Generation, |
|
|
|
|
Conditions: []*pbresource.Condition{ |
|
|
|
|
{ |
|
|
|
|
Type: workloadhealth.StatusConditionHealthy, |
|
|
|
|
State: pbresource.Condition_STATE_TRUE, |
|
|
|
|
Reason: "HEALTH_PASSING", |
|
|
|
|
// Update the health status of the workload
|
|
|
|
|
suite.client.WriteStatus(suite.ctx, &pbresource.WriteStatusRequest{ |
|
|
|
|
Id: workload.Id, |
|
|
|
|
Key: workloadhealth.StatusKey, |
|
|
|
|
Status: &pbresource.Status{ |
|
|
|
|
ObservedGeneration: workload.Generation, |
|
|
|
|
Conditions: []*pbresource.Condition{ |
|
|
|
|
{ |
|
|
|
|
Type: workloadhealth.StatusConditionHealthy, |
|
|
|
|
State: pbresource.Condition_STATE_TRUE, |
|
|
|
|
Reason: "HEALTH_PASSING", |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
// Wait for the endpoints to be regenerated
|
|
|
|
|
endpoints = suite.client.WaitForNewVersion(suite.T(), endpointsID, endpoints.Version) |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
// ensure the endpoint was put into the passing state
|
|
|
|
|
suite.requireEndpoints(endpoints, &pbcatalog.Endpoint{ |
|
|
|
|
TargetRef: workload.Id, |
|
|
|
|
Addresses: []*pbcatalog.WorkloadAddress{ |
|
|
|
|
{Host: "127.0.0.1", Ports: []string{"http"}}, |
|
|
|
|
}, |
|
|
|
|
Ports: map[string]*pbcatalog.WorkloadPort{ |
|
|
|
|
"http": {Port: 8080, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, |
|
|
|
|
}, |
|
|
|
|
HealthStatus: pbcatalog.Health_HEALTH_PASSING, |
|
|
|
|
Identity: "api", |
|
|
|
|
}) |
|
|
|
|
// Wait for the endpoints to be regenerated
|
|
|
|
|
endpoints = suite.client.WaitForNewVersion(suite.T(), endpointsID, endpoints.Version) |
|
|
|
|
|
|
|
|
|
// Update workload identity and check that the status on the service is updated
|
|
|
|
|
workload = rtest.Resource(pbcatalog.WorkloadType, "api-1"). |
|
|
|
|
WithData(suite.T(), &pbcatalog.Workload{ |
|
|
|
|
Addresses: []*pbcatalog.WorkloadAddress{{Host: "127.0.0.1"}}, |
|
|
|
|
// ensure the endpoint was put into the passing state
|
|
|
|
|
suite.requireEndpoints(endpoints, &pbcatalog.Endpoint{ |
|
|
|
|
TargetRef: workload.Id, |
|
|
|
|
Addresses: []*pbcatalog.WorkloadAddress{ |
|
|
|
|
{Host: "127.0.0.1", Ports: []string{"http"}}, |
|
|
|
|
}, |
|
|
|
|
Ports: map[string]*pbcatalog.WorkloadPort{ |
|
|
|
|
"http": {Port: 8080, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, |
|
|
|
|
"grpc": {Port: 8081, Protocol: pbcatalog.Protocol_PROTOCOL_GRPC}, |
|
|
|
|
}, |
|
|
|
|
Identity: "endpoints-api-identity", |
|
|
|
|
}). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
|
|
|
|
|
suite.client.WaitForStatusCondition(suite.T(), service.Id, StatusKey, ConditionIdentitiesFound([]string{"endpoints-api-identity"})) |
|
|
|
|
|
|
|
|
|
// Verify that the generated endpoints now contain the workload
|
|
|
|
|
endpoints = suite.client.WaitForNewVersion(suite.T(), endpointsID, endpoints.Version) |
|
|
|
|
suite.requireEndpoints(endpoints, &pbcatalog.Endpoint{ |
|
|
|
|
TargetRef: workload.Id, |
|
|
|
|
Addresses: []*pbcatalog.WorkloadAddress{ |
|
|
|
|
{Host: "127.0.0.1", Ports: []string{"http"}}, |
|
|
|
|
}, |
|
|
|
|
Ports: map[string]*pbcatalog.WorkloadPort{ |
|
|
|
|
"http": {Port: 8080, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, |
|
|
|
|
}, |
|
|
|
|
HealthStatus: pbcatalog.Health_HEALTH_PASSING, |
|
|
|
|
Identity: "endpoints-api-identity", |
|
|
|
|
}) |
|
|
|
|
HealthStatus: pbcatalog.Health_HEALTH_PASSING, |
|
|
|
|
Identity: "api", |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
// rewrite the service to add more selection criteria. This should trigger
|
|
|
|
|
// reconciliation but shouldn't result in updating the endpoints because
|
|
|
|
|
// the actual list of currently selected workloads has not changed
|
|
|
|
|
rtest.Resource(pbcatalog.ServiceType, "api"). |
|
|
|
|
WithData(suite.T(), &pbcatalog.Service{ |
|
|
|
|
Workloads: &pbcatalog.WorkloadSelector{ |
|
|
|
|
Prefixes: []string{"api-"}, |
|
|
|
|
Names: []string{"doesnt-matter"}, |
|
|
|
|
// Update workload identity and check that the status on the service is updated
|
|
|
|
|
workload = rtest.Resource(pbcatalog.WorkloadType, "api-1").WithTenancy(tenancy). |
|
|
|
|
WithData(suite.T(), &pbcatalog.Workload{ |
|
|
|
|
Addresses: []*pbcatalog.WorkloadAddress{{Host: "127.0.0.1"}}, |
|
|
|
|
Ports: map[string]*pbcatalog.WorkloadPort{ |
|
|
|
|
"http": {Port: 8080, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, |
|
|
|
|
"grpc": {Port: 8081, Protocol: pbcatalog.Protocol_PROTOCOL_GRPC}, |
|
|
|
|
}, |
|
|
|
|
Identity: "endpoints-api-identity", |
|
|
|
|
}). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
|
|
|
|
|
suite.client.WaitForStatusCondition(suite.T(), service.Id, StatusKey, ConditionIdentitiesFound([]string{"endpoints-api-identity"})) |
|
|
|
|
|
|
|
|
|
// Verify that the generated endpoints now contain the workload
|
|
|
|
|
endpoints = suite.client.WaitForNewVersion(suite.T(), endpointsID, endpoints.Version) |
|
|
|
|
suite.requireEndpoints(endpoints, &pbcatalog.Endpoint{ |
|
|
|
|
TargetRef: workload.Id, |
|
|
|
|
Addresses: []*pbcatalog.WorkloadAddress{ |
|
|
|
|
{Host: "127.0.0.1", Ports: []string{"http"}}, |
|
|
|
|
}, |
|
|
|
|
Ports: []*pbcatalog.ServicePort{ |
|
|
|
|
{TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, |
|
|
|
|
Ports: map[string]*pbcatalog.WorkloadPort{ |
|
|
|
|
"http": {Port: 8080, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, |
|
|
|
|
}, |
|
|
|
|
}). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
HealthStatus: pbcatalog.Health_HEALTH_PASSING, |
|
|
|
|
Identity: "endpoints-api-identity", |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
// Wait for the service status' observed generation to get bumped
|
|
|
|
|
service = suite.client.WaitForReconciliation(suite.T(), service.Id, StatusKey) |
|
|
|
|
// rewrite the service to add more selection criteria. This should trigger
|
|
|
|
|
// reconciliation but shouldn't result in updating the endpoints because
|
|
|
|
|
// the actual list of currently selected workloads has not changed
|
|
|
|
|
rtest.Resource(pbcatalog.ServiceType, "api").WithTenancy(tenancy). |
|
|
|
|
WithData(suite.T(), &pbcatalog.Service{ |
|
|
|
|
Workloads: &pbcatalog.WorkloadSelector{ |
|
|
|
|
Prefixes: []string{"api-"}, |
|
|
|
|
Names: []string{"doesnt-matter"}, |
|
|
|
|
}, |
|
|
|
|
Ports: []*pbcatalog.ServicePort{ |
|
|
|
|
{TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, |
|
|
|
|
}, |
|
|
|
|
}). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
|
|
|
|
|
// Verify that the endpoints were not regenerated
|
|
|
|
|
suite.client.RequireVersionUnchanged(suite.T(), endpointsID, endpoints.Version) |
|
|
|
|
// Wait for the service status' observed generation to get bumped
|
|
|
|
|
service = suite.client.WaitForReconciliation(suite.T(), service.Id, StatusKey) |
|
|
|
|
|
|
|
|
|
// Update the service.
|
|
|
|
|
updatedService := rtest.Resource(pbcatalog.ServiceType, "api"). |
|
|
|
|
WithData(suite.T(), &pbcatalog.Service{ |
|
|
|
|
Workloads: &pbcatalog.WorkloadSelector{ |
|
|
|
|
Prefixes: []string{"api-"}, |
|
|
|
|
}, |
|
|
|
|
Ports: []*pbcatalog.ServicePort{ |
|
|
|
|
{TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, |
|
|
|
|
{TargetPort: "grpc", Protocol: pbcatalog.Protocol_PROTOCOL_GRPC}, |
|
|
|
|
}, |
|
|
|
|
}). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
// Verify that the endpoints were not regenerated
|
|
|
|
|
suite.client.RequireVersionUnchanged(suite.T(), endpointsID, endpoints.Version) |
|
|
|
|
|
|
|
|
|
// Wait for the endpoints to be regenerated
|
|
|
|
|
endpoints = suite.client.WaitForNewVersion(suite.T(), endpointsID, endpoints.Version) |
|
|
|
|
rtest.RequireOwner(suite.T(), endpoints, updatedService.Id, false) |
|
|
|
|
// Update the service.
|
|
|
|
|
updatedService := rtest.Resource(pbcatalog.ServiceType, "api"). |
|
|
|
|
WithTenancy(tenancy). |
|
|
|
|
WithData(suite.T(), &pbcatalog.Service{ |
|
|
|
|
Workloads: &pbcatalog.WorkloadSelector{ |
|
|
|
|
Prefixes: []string{"api-"}, |
|
|
|
|
}, |
|
|
|
|
Ports: []*pbcatalog.ServicePort{ |
|
|
|
|
{TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, |
|
|
|
|
{TargetPort: "grpc", Protocol: pbcatalog.Protocol_PROTOCOL_GRPC}, |
|
|
|
|
}, |
|
|
|
|
}). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
|
|
|
|
|
// Delete the endpoints. The controller should bring these back momentarily
|
|
|
|
|
suite.client.Delete(suite.ctx, &pbresource.DeleteRequest{Id: endpointsID}) |
|
|
|
|
// Wait for the endpoints to be regenerated
|
|
|
|
|
endpoints = suite.client.WaitForNewVersion(suite.T(), endpointsID, endpoints.Version) |
|
|
|
|
rtest.RequireOwner(suite.T(), endpoints, updatedService.Id, false) |
|
|
|
|
|
|
|
|
|
// Wait for controller to recreate the endpoints
|
|
|
|
|
retry.Run(suite.T(), func(r *retry.R) { |
|
|
|
|
suite.client.RequireResourceExists(r, endpointsID) |
|
|
|
|
}) |
|
|
|
|
// Delete the endpoints. The controller should bring these back momentarily
|
|
|
|
|
suite.client.Delete(suite.ctx, &pbresource.DeleteRequest{Id: endpointsID}) |
|
|
|
|
|
|
|
|
|
// Move the service to having unmanaged endpoints
|
|
|
|
|
rtest.Resource(pbcatalog.ServiceType, "api"). |
|
|
|
|
WithData(suite.T(), &pbcatalog.Service{ |
|
|
|
|
Ports: []*pbcatalog.ServicePort{ |
|
|
|
|
{TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, |
|
|
|
|
}, |
|
|
|
|
}). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
// Wait for controller to recreate the endpoints
|
|
|
|
|
retry.Run(suite.T(), func(r *retry.R) { |
|
|
|
|
suite.client.RequireResourceExists(r, endpointsID) |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
res = suite.client.WaitForReconciliation(suite.T(), service.Id, StatusKey) |
|
|
|
|
rtest.RequireStatusCondition(suite.T(), res, StatusKey, ConditionUnmanaged) |
|
|
|
|
// Move the service to having unmanaged endpoints
|
|
|
|
|
rtest.Resource(pbcatalog.ServiceType, "api"). |
|
|
|
|
WithTenancy(tenancy). |
|
|
|
|
WithData(suite.T(), &pbcatalog.Service{ |
|
|
|
|
Ports: []*pbcatalog.ServicePort{ |
|
|
|
|
{TargetPort: "http", Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, |
|
|
|
|
}, |
|
|
|
|
}). |
|
|
|
|
Write(suite.T(), suite.client) |
|
|
|
|
|
|
|
|
|
res = suite.client.WaitForReconciliation(suite.T(), service.Id, StatusKey) |
|
|
|
|
rtest.RequireStatusCondition(suite.T(), res, StatusKey, ConditionUnmanaged) |
|
|
|
|
|
|
|
|
|
// Verify that the endpoints were deleted
|
|
|
|
|
suite.client.RequireResourceNotFound(suite.T(), endpointsID) |
|
|
|
|
// Verify that the endpoints were deleted
|
|
|
|
|
suite.client.RequireResourceNotFound(suite.T(), endpointsID) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func TestController(t *testing.T) { |
|
|
|
|
suite.Run(t, new(controllerSuite)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (suite *controllerSuite) runTestCaseWithTenancies(testFunc func(*pbresource.Tenancy)) { |
|
|
|
|
for _, tenancy := range suite.tenancies { |
|
|
|
|
suite.Run(suite.appendTenancyInfo(tenancy), func() { |
|
|
|
|
testFunc(tenancy) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (suite *controllerSuite) appendTenancyInfo(tenancy *pbresource.Tenancy) string { |
|
|
|
|
return fmt.Sprintf("%s_Namespace_%s_Partition", tenancy.Namespace, tenancy.Partition) |
|
|
|
|
} |
|
|
|
|