diff --git a/internal/multicluster/internal/controllers/v1compat/controller.go b/internal/multicluster/internal/controllers/v1compat/controller.go index e5c64a6642..322a5071f1 100644 --- a/internal/multicluster/internal/controllers/v1compat/controller.go +++ b/internal/multicluster/internal/controllers/v1compat/controller.go @@ -6,8 +6,11 @@ package v1compat import ( "context" "fmt" + "slices" "sort" + "golang.org/x/exp/maps" + "github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/internal/controller" @@ -25,6 +28,7 @@ const ( controllerMetaKey = "managed-by-controller" ) +//go:generate mockery --name AggregatedConfig --inpackage --with-expecter --filename mock_AggregatedConfig.go type AggregatedConfig interface { Start(context.Context) GetExportedServicesConfigEntry(context.Context, string, *acl.EnterpriseMeta) (*structs.ExportedServicesConfigEntry, error) @@ -87,7 +91,9 @@ func (r *reconciler) Reconcile(ctx context.Context, rt controller.Runtime, req c entMeta.OverridePartition(req.ID.Tenancy.Partition) existing, err := r.config.GetExportedServicesConfigEntry(ctx, req.ID.Tenancy.Partition, entMeta) if err != nil { - rt.Logger.Error("error getting exported service config entry", "error", err) + // When we can't read the existing exported-services we purposely allow + // reconciler to continue so we can still write a new one + rt.Logger.Warn("error getting exported service config entry but continuing reconcile", "error", err) } if existing != nil && existing.Meta["managed-by-controller"] != ControllerName { @@ -117,7 +123,6 @@ func (r *reconciler) Reconcile(ctx context.Context, rt controller.Runtime, req c }, index.IndexQueryOptions{Prefix: true}, ) - if err != nil { rt.Logger.Error("error retrieving partition exported services", "error", err) return err @@ -133,9 +138,8 @@ func (r *reconciler) Reconcile(ctx context.Context, rt controller.Runtime, req c }, index.IndexQueryOptions{Prefix: true}, ) - if err != nil { - rt.Logger.Error("error retrieving namespace exported service", "error", err) + rt.Logger.Error("error retrieving namespace exported services", "error", err) return err } @@ -149,7 +153,6 @@ func (r *reconciler) Reconcile(ctx context.Context, rt controller.Runtime, req c }, index.IndexQueryOptions{Prefix: true}, ) - if err != nil { rt.Logger.Error("error retrieving exported services", "error", err) return err @@ -243,24 +246,24 @@ func (c *exportConsumers) addConsumers(consumers []*pbmulticluster.ExportedServi func (c *exportConsumers) configEntryConsumers() []structs.ServiceConsumer { consumers := make([]structs.ServiceConsumer, 0, len(c.partitions)+len(c.peers)+len(c.samenessGroups)) - partitions := keys(c.partitions) - sort.Strings(partitions) + partitions := maps.Keys(c.partitions) + slices.Sort(partitions) for _, consumer := range partitions { consumers = append(consumers, structs.ServiceConsumer{ Partition: consumer, }) } - peers := keys(c.peers) - sort.Strings(peers) + peers := maps.Keys(c.peers) + slices.Sort(peers) for _, consumer := range peers { consumers = append(consumers, structs.ServiceConsumer{ Peer: consumer, }) } - samenessGroups := keys(c.samenessGroups) - sort.Strings(samenessGroups) + samenessGroups := maps.Keys(c.samenessGroups) + slices.Sort(samenessGroups) for _, consumer := range samenessGroups { consumers = append(consumers, structs.ServiceConsumer{ SamenessGroup: consumer, @@ -316,8 +319,8 @@ func (t *exportTracker) allExports() []structs.ExportedService { }) } - namespaces := keys(t.namespaces) - sort.Strings(namespaces) + namespaces := maps.Keys(t.namespaces) + slices.Sort(namespaces) for _, ns := range namespaces { exports = append(exports, structs.ExportedService{ Name: "*", @@ -326,7 +329,7 @@ func (t *exportTracker) allExports() []structs.ExportedService { }) } - services := keys(t.services) + services := maps.Keys(t.services) sort.Slice(services, func(i, j int) bool { // the partitions must already be equal because we are only // looking at resource exports for a single partition. diff --git a/internal/multicluster/internal/controllers/v1compat/controller_test.go b/internal/multicluster/internal/controllers/v1compat/controller_test.go new file mode 100644 index 0000000000..a70e1028b6 --- /dev/null +++ b/internal/multicluster/internal/controllers/v1compat/controller_test.go @@ -0,0 +1,428 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package v1compat + +import ( + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + "github.com/hashicorp/consul/acl" + svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing" + "github.com/hashicorp/consul/agent/structs" + "github.com/hashicorp/consul/internal/catalog" + "github.com/hashicorp/consul/internal/controller" + "github.com/hashicorp/consul/internal/multicluster/internal/types" + "github.com/hashicorp/consul/internal/resource" + rtest "github.com/hashicorp/consul/internal/resource/resourcetest" + pbmulticluster "github.com/hashicorp/consul/proto-public/pbmulticluster/v2" + "github.com/hashicorp/consul/proto-public/pbresource" + "github.com/hashicorp/consul/sdk/testutil" + "github.com/hashicorp/consul/version/versiontest" +) + +type controllerSuite struct { + suite.Suite + ctx context.Context + ctl *controller.TestController + isEnterprise bool + tenancies []*pbresource.Tenancy + config *MockAggregatedConfig +} + +func (suite *controllerSuite) SetupTest() { + suite.tenancies = rtest.TestTenancies() + suite.isEnterprise = versiontest.IsEnterprise() + suite.ctx = testutil.TestContext(suite.T()) + client := svctest.NewResourceServiceBuilder(). + WithRegisterFns(types.Register, catalog.RegisterTypes). + WithTenancies(suite.tenancies...). + Run(suite.T()) + + suite.config = NewMockAggregatedConfig(suite.T()) + suite.config.EXPECT().EventChannel().Return(make(chan controller.Event)) + suite.ctl = controller.NewTestController( + Controller(suite.config), + client, + ).WithLogger(testutil.Logger(suite.T())) +} + +// Test that we do nothing if V1 exports have not been replicated to v2 resources compatible with this controller +func (suite *controllerSuite) TestReconcile_V1ExportsExist() { + incompatibleConfig := &structs.ExportedServicesConfigEntry{ + Name: "v1Legacy", + Meta: map[string]string{controllerMetaKey: "foo-controller"}, + } + + suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) { + entMeta := acl.DefaultEnterpriseMeta() + entMeta.OverridePartition(tenancy.Partition) + + suite.config.EXPECT(). + GetExportedServicesConfigEntry(suite.ctx, tenancy.Partition, entMeta). + Return(incompatibleConfig, nil) + + resID := &pbresource.ID{ + Type: pbmulticluster.ComputedExportedServicesType, + Tenancy: &pbresource.Tenancy{Partition: tenancy.Partition}, + Name: types.ComputedExportedServicesName, + } + err := suite.ctl.Reconcile(suite.ctx, controller.Request{ID: resID}) + require.NoError(suite.T(), err) + }) +} + +// Test that we do not stop reconciler even when we fail to retrieve the config entry +func (suite *controllerSuite) TestReconcile_GetExportedServicesConfigEntry_Error() { + suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) { + entMeta := acl.DefaultEnterpriseMeta() + entMeta.OverridePartition(tenancy.Partition) + + suite.config.EXPECT(). + GetExportedServicesConfigEntry(suite.ctx, tenancy.Partition, entMeta). + Return(nil, fmt.Errorf("failed to retrieve config entry")) + + resID := &pbresource.ID{ + Type: pbmulticluster.ComputedExportedServicesType, + Tenancy: &pbresource.Tenancy{Partition: tenancy.Partition}, + Name: types.ComputedExportedServicesName, + } + err := suite.ctl.Reconcile(suite.ctx, controller.Request{ID: resID}) + require.NoError(suite.T(), err) + }) +} + +// Delete config entry for case where resources aren't found +func (suite *controllerSuite) TestReconcile_DeleteConfig_MissingResources() { + suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) { + entMeta := acl.DefaultEnterpriseMeta() + entMeta.OverridePartition(tenancy.Partition) + + configEntry := &structs.ExportedServicesConfigEntry{ + // v1 exported-services config entries must have a Name that is the partitions name + Name: tenancy.Partition, + Meta: map[string]string{ + controllerMetaKey: ControllerName, + }, + EnterpriseMeta: acl.NewEnterpriseMetaWithPartition(tenancy.Partition, ""), + } + + suite.config.EXPECT().GetExportedServicesConfigEntry(suite.ctx, tenancy.Partition, entMeta).Return(configEntry, nil) + suite.config.EXPECT().DeleteExportedServicesConfigEntry(suite.ctx, tenancy.Partition, entMeta).Return(nil) + + resID := &pbresource.ID{ + Type: pbmulticluster.ComputedExportedServicesType, + Tenancy: &pbresource.Tenancy{Partition: tenancy.Partition}, + Name: types.ComputedExportedServicesName, + } + err := suite.ctl.Reconcile(suite.ctx, controller.Request{ID: resID}) + require.NoError(suite.T(), err) + }) +} + +func (suite *controllerSuite) TestReconcile_NewExport_PartitionExport() { + if !suite.isEnterprise { + suite.T().Skip("this test should only run against the enterprise build") + } + + suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) { + entMeta := acl.DefaultEnterpriseMeta() + entMeta.OverridePartition(tenancy.Partition) + + // used as a return value for GetExportedServicesConfigEntry + existingCE := &structs.ExportedServicesConfigEntry{ + // v1 exported-services config entries must have a Name that is the partitions name + Name: tenancy.Partition, + Meta: map[string]string{ + controllerMetaKey: ControllerName, + }, + EnterpriseMeta: acl.NewEnterpriseMetaWithPartition(tenancy.Partition, ""), + } + suite.config.EXPECT().GetExportedServicesConfigEntry(suite.ctx, tenancy.Partition, entMeta).Return(existingCE, nil) + + // expected config entry to be written by reconcile + expectedCE := &structs.ExportedServicesConfigEntry{ + Name: tenancy.Partition, + Services: []structs.ExportedService{ + { + Name: "s1", + Namespace: resource.DefaultNamespaceName, + Consumers: []structs.ServiceConsumer{ + { + Partition: "p1", + }, + }, + }, + }, + Meta: map[string]string{ + controllerMetaKey: ControllerName, + }, + EnterpriseMeta: *entMeta, + } + suite.config.EXPECT().WriteExportedServicesConfigEntry(suite.ctx, expectedCE).Return(nil) + + name := "s1" + expSv := &pbmulticluster.ExportedServices{ + Services: []string{name}, + Consumers: []*pbmulticluster.ExportedServicesConsumer{ + {ConsumerTenancy: &pbmulticluster.ExportedServicesConsumer_Partition{Partition: "p1"}}, + }, + } + rtest.Resource(pbmulticluster.ExportedServicesType, "exported-svcs"). + WithData(suite.T(), expSv). + WithTenancy(&pbresource.Tenancy{Partition: tenancy.Partition}). + Write(suite.T(), suite.ctl.Runtime().Client) + cesID := &pbresource.ID{ + Type: pbmulticluster.ComputedExportedServicesType, + Tenancy: &pbresource.Tenancy{Partition: tenancy.Partition}, + Name: types.ComputedExportedServicesName, + } + err := suite.ctl.Reconcile(suite.ctx, controller.Request{ID: cesID}) + require.NoError(suite.T(), err) + }) +} + +func (suite *controllerSuite) TestReconcile_NewExport_PeerExport() { + if !suite.isEnterprise { + suite.T().Skip("this test should only run against the enterprise build") + } + + suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) { + entMeta := acl.DefaultEnterpriseMeta() + entMeta.OverridePartition(tenancy.Partition) + + // used as a return value for GetExportedServicesConfigEntry + existingCE := &structs.ExportedServicesConfigEntry{ + // v1 exported-services config entries must have a Name that is the partitions name + Name: tenancy.Partition, + Meta: map[string]string{ + controllerMetaKey: ControllerName, + }, + EnterpriseMeta: acl.NewEnterpriseMetaWithPartition(tenancy.Partition, ""), + } + suite.config.EXPECT().GetExportedServicesConfigEntry(suite.ctx, tenancy.Partition, entMeta).Return(existingCE, nil) + + // expected config entry to be written by reconcile + expectedCE := &structs.ExportedServicesConfigEntry{ + Name: tenancy.Partition, + Services: []structs.ExportedService{ + { + Name: "s1", + Namespace: resource.DefaultNamespaceName, + Consumers: []structs.ServiceConsumer{ + { + Peer: "peer1", + }, + }, + }, + }, + Meta: map[string]string{ + controllerMetaKey: ControllerName, + }, + EnterpriseMeta: *entMeta, + } + suite.config.EXPECT().WriteExportedServicesConfigEntry(suite.ctx, expectedCE).Return(nil) + + name := "s1" + expSv := &pbmulticluster.ExportedServices{ + Services: []string{name}, + Consumers: []*pbmulticluster.ExportedServicesConsumer{ + {ConsumerTenancy: &pbmulticluster.ExportedServicesConsumer_Peer{Peer: "peer1"}}, + }, + } + rtest.Resource(pbmulticluster.ExportedServicesType, "exported-svcs"). + WithData(suite.T(), expSv). + WithTenancy(&pbresource.Tenancy{Partition: tenancy.Partition}). + Write(suite.T(), suite.ctl.Runtime().Client) + cesID := &pbresource.ID{ + Type: pbmulticluster.ComputedExportedServicesType, + Tenancy: &pbresource.Tenancy{Partition: tenancy.Partition}, + Name: types.ComputedExportedServicesName, + } + err := suite.ctl.Reconcile(suite.ctx, controller.Request{ID: cesID}) + require.NoError(suite.T(), err) + }) +} + +func (suite *controllerSuite) TestReconcile_NewExport_SamenessGroupsExport() { + if !suite.isEnterprise { + suite.T().Skip("this test should only run against the enterprise build") + } + + suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) { + entMeta := acl.DefaultEnterpriseMeta() + entMeta.OverridePartition(tenancy.Partition) + + // used as a return value for GetExportedServicesConfigEntry + existingCE := &structs.ExportedServicesConfigEntry{ + // v1 exported-services config entries must have a Name that is the partitions name + Name: tenancy.Partition, + Meta: map[string]string{ + controllerMetaKey: ControllerName, + }, + EnterpriseMeta: acl.NewEnterpriseMetaWithPartition(tenancy.Partition, ""), + } + suite.config.EXPECT().GetExportedServicesConfigEntry(suite.ctx, tenancy.Partition, entMeta).Return(existingCE, nil) + + // expected config entry to be written by reconcile + expectedCE := &structs.ExportedServicesConfigEntry{ + Name: tenancy.Partition, + Services: []structs.ExportedService{ + { + Name: "s1", + Namespace: resource.DefaultNamespaceName, + Consumers: []structs.ServiceConsumer{ + { + SamenessGroup: "sg1", + }, + }, + }, + }, + Meta: map[string]string{ + controllerMetaKey: ControllerName, + }, + EnterpriseMeta: *entMeta, + } + suite.config.EXPECT().WriteExportedServicesConfigEntry(suite.ctx, expectedCE).Return(nil) + + name := "s1" + expSv := &pbmulticluster.ExportedServices{ + Services: []string{name}, + Consumers: []*pbmulticluster.ExportedServicesConsumer{ + {ConsumerTenancy: &pbmulticluster.ExportedServicesConsumer_SamenessGroup{SamenessGroup: "sg1"}}, + }, + } + rtest.Resource(pbmulticluster.ExportedServicesType, "exported-svcs"). + WithData(suite.T(), expSv). + WithTenancy(&pbresource.Tenancy{Partition: tenancy.Partition}). + Write(suite.T(), suite.ctl.Runtime().Client) + cesID := &pbresource.ID{ + Type: pbmulticluster.ComputedExportedServicesType, + Tenancy: &pbresource.Tenancy{Partition: tenancy.Partition}, + Name: types.ComputedExportedServicesName, + } + err := suite.ctl.Reconcile(suite.ctx, controller.Request{ID: cesID}) + require.NoError(suite.T(), err) + }) +} + +func (suite *controllerSuite) TestReconcile_MultipleExports() { + if !suite.isEnterprise { + suite.T().Skip("this test should only run against the enterprise build") + } + + suite.runTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) { + entMeta := acl.DefaultEnterpriseMeta() + entMeta.OverridePartition(tenancy.Partition) + configCE := &structs.ExportedServicesConfigEntry{ + // v1 exported-services config entries must have a Name that is the partitions name + Name: tenancy.Partition, + Meta: map[string]string{ + controllerMetaKey: ControllerName, + }, + EnterpriseMeta: acl.NewEnterpriseMetaWithPartition(tenancy.Partition, ""), + } + + suite.config.EXPECT(). + GetExportedServicesConfigEntry(suite.ctx, tenancy.Partition, entMeta). + Return(configCE, nil) + + expSv1 := &pbmulticluster.ExportedServices{ + Services: []string{"s1"}, + Consumers: []*pbmulticluster.ExportedServicesConsumer{ + {ConsumerTenancy: &pbmulticluster.ExportedServicesConsumer_Partition{Partition: "p1"}}, + {ConsumerTenancy: &pbmulticluster.ExportedServicesConsumer_Partition{Partition: "p4"}}, + }, + } + expSv2 := &pbmulticluster.ExportedServices{ + Services: []string{"s2"}, + Consumers: []*pbmulticluster.ExportedServicesConsumer{ + {ConsumerTenancy: &pbmulticluster.ExportedServicesConsumer_Partition{Partition: "p2"}}, + {ConsumerTenancy: &pbmulticluster.ExportedServicesConsumer_Peer{Peer: "peer1"}}, + }, + } + expSv3 := &pbmulticluster.ExportedServices{ + Services: []string{"s1", "s3"}, + Consumers: []*pbmulticluster.ExportedServicesConsumer{ + {ConsumerTenancy: &pbmulticluster.ExportedServicesConsumer_Partition{Partition: "p3"}}, + {ConsumerTenancy: &pbmulticluster.ExportedServicesConsumer_SamenessGroup{SamenessGroup: "sg1"}}, + }, + } + + for i, s := range []*pbmulticluster.ExportedServices{expSv1, expSv2, expSv3} { + rtest.Resource(pbmulticluster.ExportedServicesType, fmt.Sprintf("exported-svcs-%d", i)). + WithData(suite.T(), s). + WithTenancy(&pbresource.Tenancy{Partition: tenancy.Partition}). + Write(suite.T(), suite.ctl.Runtime().Client) + } + + cesID := &pbresource.ID{ + Type: pbmulticluster.ComputedExportedServicesType, + Tenancy: &pbresource.Tenancy{Partition: tenancy.Partition}, + Name: types.ComputedExportedServicesName, + } + + // expected computed config entry to be written by reconcile + computedConfigEntry := &structs.ExportedServicesConfigEntry{ + Name: tenancy.Partition, + Meta: map[string]string{ + controllerMetaKey: ControllerName, + }, + Services: []structs.ExportedService{ + { + Name: "s3", + Namespace: resource.DefaultNamespaceName, + Consumers: []structs.ServiceConsumer{ + {Partition: "p3"}, + {SamenessGroup: "sg1"}, + }, + }, + { + Name: "s2", + Namespace: resource.DefaultNamespaceName, + Consumers: []structs.ServiceConsumer{ + {Partition: "p2"}, + {Peer: "peer1"}, + }, + }, + { + Name: "s1", + Namespace: resource.DefaultNamespaceName, + Consumers: []structs.ServiceConsumer{ + {Partition: "p1"}, + {Partition: "p3"}, + {Partition: "p4"}, + {SamenessGroup: "sg1"}, + }, + }, + }, + EnterpriseMeta: acl.NewEnterpriseMetaWithPartition(tenancy.Partition, resource.DefaultNamespaceName), + } + suite.config.EXPECT(). + WriteExportedServicesConfigEntry(suite.ctx, computedConfigEntry). + Return(nil) + + err := suite.ctl.Reconcile(suite.ctx, controller.Request{ID: cesID}) + require.NoError(suite.T(), err) + }) +} + +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) +} + +func TestController(t *testing.T) { + suite.Run(t, new(controllerSuite)) +} diff --git a/internal/multicluster/internal/controllers/v1compat/mock_AggregatedConfig.go b/internal/multicluster/internal/controllers/v1compat/mock_AggregatedConfig.go new file mode 100644 index 0000000000..4d33ce5858 --- /dev/null +++ b/internal/multicluster/internal/controllers/v1compat/mock_AggregatedConfig.go @@ -0,0 +1,262 @@ +// Code generated by mockery v2.20.0. DO NOT EDIT. + +package v1compat + +import ( + context "context" + + acl "github.com/hashicorp/consul/acl" + + controller "github.com/hashicorp/consul/internal/controller" + + mock "github.com/stretchr/testify/mock" + + structs "github.com/hashicorp/consul/agent/structs" +) + +// MockAggregatedConfig is an autogenerated mock type for the AggregatedConfig type +type MockAggregatedConfig struct { + mock.Mock +} + +type MockAggregatedConfig_Expecter struct { + mock *mock.Mock +} + +func (_m *MockAggregatedConfig) EXPECT() *MockAggregatedConfig_Expecter { + return &MockAggregatedConfig_Expecter{mock: &_m.Mock} +} + +// DeleteExportedServicesConfigEntry provides a mock function with given fields: _a0, _a1, _a2 +func (_m *MockAggregatedConfig) DeleteExportedServicesConfigEntry(_a0 context.Context, _a1 string, _a2 *acl.EnterpriseMeta) error { + ret := _m.Called(_a0, _a1, _a2) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, *acl.EnterpriseMeta) error); ok { + r0 = rf(_a0, _a1, _a2) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockAggregatedConfig_DeleteExportedServicesConfigEntry_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteExportedServicesConfigEntry' +type MockAggregatedConfig_DeleteExportedServicesConfigEntry_Call struct { + *mock.Call +} + +// DeleteExportedServicesConfigEntry is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 string +// - _a2 *acl.EnterpriseMeta +func (_e *MockAggregatedConfig_Expecter) DeleteExportedServicesConfigEntry(_a0 interface{}, _a1 interface{}, _a2 interface{}) *MockAggregatedConfig_DeleteExportedServicesConfigEntry_Call { + return &MockAggregatedConfig_DeleteExportedServicesConfigEntry_Call{Call: _e.mock.On("DeleteExportedServicesConfigEntry", _a0, _a1, _a2)} +} + +func (_c *MockAggregatedConfig_DeleteExportedServicesConfigEntry_Call) Run(run func(_a0 context.Context, _a1 string, _a2 *acl.EnterpriseMeta)) *MockAggregatedConfig_DeleteExportedServicesConfigEntry_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(*acl.EnterpriseMeta)) + }) + return _c +} + +func (_c *MockAggregatedConfig_DeleteExportedServicesConfigEntry_Call) Return(_a0 error) *MockAggregatedConfig_DeleteExportedServicesConfigEntry_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockAggregatedConfig_DeleteExportedServicesConfigEntry_Call) RunAndReturn(run func(context.Context, string, *acl.EnterpriseMeta) error) *MockAggregatedConfig_DeleteExportedServicesConfigEntry_Call { + _c.Call.Return(run) + return _c +} + +// EventChannel provides a mock function with given fields: +func (_m *MockAggregatedConfig) EventChannel() chan controller.Event { + ret := _m.Called() + + var r0 chan controller.Event + if rf, ok := ret.Get(0).(func() chan controller.Event); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(chan controller.Event) + } + } + + return r0 +} + +// MockAggregatedConfig_EventChannel_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EventChannel' +type MockAggregatedConfig_EventChannel_Call struct { + *mock.Call +} + +// EventChannel is a helper method to define mock.On call +func (_e *MockAggregatedConfig_Expecter) EventChannel() *MockAggregatedConfig_EventChannel_Call { + return &MockAggregatedConfig_EventChannel_Call{Call: _e.mock.On("EventChannel")} +} + +func (_c *MockAggregatedConfig_EventChannel_Call) Run(run func()) *MockAggregatedConfig_EventChannel_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockAggregatedConfig_EventChannel_Call) Return(_a0 chan controller.Event) *MockAggregatedConfig_EventChannel_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockAggregatedConfig_EventChannel_Call) RunAndReturn(run func() chan controller.Event) *MockAggregatedConfig_EventChannel_Call { + _c.Call.Return(run) + return _c +} + +// GetExportedServicesConfigEntry provides a mock function with given fields: _a0, _a1, _a2 +func (_m *MockAggregatedConfig) GetExportedServicesConfigEntry(_a0 context.Context, _a1 string, _a2 *acl.EnterpriseMeta) (*structs.ExportedServicesConfigEntry, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 *structs.ExportedServicesConfigEntry + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, *acl.EnterpriseMeta) (*structs.ExportedServicesConfigEntry, error)); ok { + return rf(_a0, _a1, _a2) + } + if rf, ok := ret.Get(0).(func(context.Context, string, *acl.EnterpriseMeta) *structs.ExportedServicesConfigEntry); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*structs.ExportedServicesConfigEntry) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, *acl.EnterpriseMeta) error); ok { + r1 = rf(_a0, _a1, _a2) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockAggregatedConfig_GetExportedServicesConfigEntry_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetExportedServicesConfigEntry' +type MockAggregatedConfig_GetExportedServicesConfigEntry_Call struct { + *mock.Call +} + +// GetExportedServicesConfigEntry is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 string +// - _a2 *acl.EnterpriseMeta +func (_e *MockAggregatedConfig_Expecter) GetExportedServicesConfigEntry(_a0 interface{}, _a1 interface{}, _a2 interface{}) *MockAggregatedConfig_GetExportedServicesConfigEntry_Call { + return &MockAggregatedConfig_GetExportedServicesConfigEntry_Call{Call: _e.mock.On("GetExportedServicesConfigEntry", _a0, _a1, _a2)} +} + +func (_c *MockAggregatedConfig_GetExportedServicesConfigEntry_Call) Run(run func(_a0 context.Context, _a1 string, _a2 *acl.EnterpriseMeta)) *MockAggregatedConfig_GetExportedServicesConfigEntry_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(*acl.EnterpriseMeta)) + }) + return _c +} + +func (_c *MockAggregatedConfig_GetExportedServicesConfigEntry_Call) Return(_a0 *structs.ExportedServicesConfigEntry, _a1 error) *MockAggregatedConfig_GetExportedServicesConfigEntry_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockAggregatedConfig_GetExportedServicesConfigEntry_Call) RunAndReturn(run func(context.Context, string, *acl.EnterpriseMeta) (*structs.ExportedServicesConfigEntry, error)) *MockAggregatedConfig_GetExportedServicesConfigEntry_Call { + _c.Call.Return(run) + return _c +} + +// Start provides a mock function with given fields: _a0 +func (_m *MockAggregatedConfig) Start(_a0 context.Context) { + _m.Called(_a0) +} + +// MockAggregatedConfig_Start_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Start' +type MockAggregatedConfig_Start_Call struct { + *mock.Call +} + +// Start is a helper method to define mock.On call +// - _a0 context.Context +func (_e *MockAggregatedConfig_Expecter) Start(_a0 interface{}) *MockAggregatedConfig_Start_Call { + return &MockAggregatedConfig_Start_Call{Call: _e.mock.On("Start", _a0)} +} + +func (_c *MockAggregatedConfig_Start_Call) Run(run func(_a0 context.Context)) *MockAggregatedConfig_Start_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *MockAggregatedConfig_Start_Call) Return() *MockAggregatedConfig_Start_Call { + _c.Call.Return() + return _c +} + +func (_c *MockAggregatedConfig_Start_Call) RunAndReturn(run func(context.Context)) *MockAggregatedConfig_Start_Call { + _c.Call.Return(run) + return _c +} + +// WriteExportedServicesConfigEntry provides a mock function with given fields: _a0, _a1 +func (_m *MockAggregatedConfig) WriteExportedServicesConfigEntry(_a0 context.Context, _a1 *structs.ExportedServicesConfigEntry) error { + ret := _m.Called(_a0, _a1) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *structs.ExportedServicesConfigEntry) error); ok { + r0 = rf(_a0, _a1) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockAggregatedConfig_WriteExportedServicesConfigEntry_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WriteExportedServicesConfigEntry' +type MockAggregatedConfig_WriteExportedServicesConfigEntry_Call struct { + *mock.Call +} + +// WriteExportedServicesConfigEntry is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 *structs.ExportedServicesConfigEntry +func (_e *MockAggregatedConfig_Expecter) WriteExportedServicesConfigEntry(_a0 interface{}, _a1 interface{}) *MockAggregatedConfig_WriteExportedServicesConfigEntry_Call { + return &MockAggregatedConfig_WriteExportedServicesConfigEntry_Call{Call: _e.mock.On("WriteExportedServicesConfigEntry", _a0, _a1)} +} + +func (_c *MockAggregatedConfig_WriteExportedServicesConfigEntry_Call) Run(run func(_a0 context.Context, _a1 *structs.ExportedServicesConfigEntry)) *MockAggregatedConfig_WriteExportedServicesConfigEntry_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*structs.ExportedServicesConfigEntry)) + }) + return _c +} + +func (_c *MockAggregatedConfig_WriteExportedServicesConfigEntry_Call) Return(_a0 error) *MockAggregatedConfig_WriteExportedServicesConfigEntry_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockAggregatedConfig_WriteExportedServicesConfigEntry_Call) RunAndReturn(run func(context.Context, *structs.ExportedServicesConfigEntry) error) *MockAggregatedConfig_WriteExportedServicesConfigEntry_Call { + _c.Call.Return(run) + return _c +} + +type mockConstructorTestingTNewMockAggregatedConfig interface { + mock.TestingT + Cleanup(func()) +} + +// NewMockAggregatedConfig creates a new instance of MockAggregatedConfig. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewMockAggregatedConfig(t mockConstructorTestingTNewMockAggregatedConfig) *MockAggregatedConfig { + mock := &MockAggregatedConfig{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +}