From 123bc95e1a3f5b65855ca68515b0700ec9c01044 Mon Sep 17 00:00:00 2001 From: Matt Keeler Date: Wed, 13 Dec 2023 10:06:39 -0500 Subject: [PATCH] Add Common Controller Caching Infrastructure (#19767) * Add Common Controller Caching Infrastructure --- .grpcmocks.yaml | 20 + Makefile | 9 +- .../grpc-external/services/resource/delete.go | 2 +- agent/grpc-external/services/resource/list.go | 2 +- .../services/resource/list_by_owner.go | 2 +- .../services/resource/list_test.go | 13 +- agent/grpc-external/services/resource/read.go | 2 +- .../grpc-external/services/resource/server.go | 9 +- .../services/resource/server_test.go | 25 + .../grpc-external/services/resource/watch.go | 2 +- .../services/resource/watch_test.go | 13 +- .../controller-architecture/controllers.md | 91 ++- .../controller-architecture/guide.md | 10 +- go.mod | 2 + go.sum | 4 + .../pbacl/mock_ACLServiceClient.go | 180 +++++ .../pbacl/mock_ACLServiceServer.go | 147 ++++ .../pbacl/mock_UnsafeACLServiceServer.go | 64 ++ .../mock_ConnectCAServiceClient.go | 180 +++++ .../mock_ConnectCAServiceServer.go | 135 ++++ .../mock_ConnectCAService_WatchRootsClient.go | 356 ++++++++++ .../mock_ConnectCAService_WatchRootsServer.go | 325 +++++++++ .../mock_UnsafeConnectCAServiceServer.go | 64 ++ .../mock_DataplaneServiceClient.go | 180 +++++ .../mock_DataplaneServiceServer.go | 147 ++++ .../mock_UnsafeDataplaneServiceServer.go | 64 ++ ...GetEnvoyBootstrapParamsRequest_NodeSpec.go | 64 ++ .../pbdns/mock_DNSServiceClient.go | 110 +++ .../pbdns/mock_DNSServiceServer.go | 92 +++ .../pbdns/mock_UnsafeDNSServiceServer.go | 64 ++ .../pbresource/mock_ResourceServiceClient.go | 530 +++++++++++++++ .../pbresource/mock_ResourceServiceServer.go | 410 +++++++++++ .../mock_ResourceService_WatchListClient.go | 356 ++++++++++ .../mock_ResourceService_WatchListServer.go | 325 +++++++++ .../mock_UnsafeResourceServiceServer.go | 64 ++ .../mock_ServerDiscoveryServiceClient.go | 110 +++ .../mock_ServerDiscoveryServiceServer.go | 78 +++ ...rverDiscoveryService_WatchServersClient.go | 356 ++++++++++ ...rverDiscoveryService_WatchServersServer.go | 325 +++++++++ ...mock_UnsafeServerDiscoveryServiceServer.go | 64 ++ .../trafficpermissions/controller.go | 7 +- .../controllers/endpoints/controller.go | 7 +- .../controllers/failover/controller.go | 4 +- .../controllers/nodehealth/controller.go | 7 +- .../controllers/workloadhealth/controller.go | 7 +- internal/controller/.mockery.yaml | 11 + internal/controller/api.go | 260 ------- internal/controller/cache/.mockery.yaml | 15 + internal/controller/cache/cache.go | 213 ++++++ internal/controller/cache/cache_test.go | 353 ++++++++++ .../controller/cache/cachemock/mock_Cache.go | 637 ++++++++++++++++++ .../controller/cache/cachemock/mock_Query.go | 100 +++ .../cache/cachemock/mock_ReadOnlyCache.go | 432 ++++++++++++ .../cache/cachemock/mock_ResourceIterator.go | 78 +++ .../cache/cachemock/mock_WriteCache.go | 119 ++++ internal/controller/cache/client.go | 56 ++ internal/controller/cache/client_test.go | 265 ++++++++ internal/controller/cache/errors.go | 73 ++ internal/controller/cache/errors_test.go | 59 ++ internal/controller/cache/index/.mockery.yaml | 23 + internal/controller/cache/index/builder.go | 33 + .../controller/cache/index/builder_test.go | 33 + .../controller/cache/index/convenience.go | 132 ++++ .../cache/index/convenience_test.go | 334 +++++++++ internal/controller/cache/index/errors.go | 16 + .../controller/cache/index/errors_test.go | 16 + internal/controller/cache/index/index.go | 84 +++ internal/controller/cache/index/index_test.go | 137 ++++ .../cache/index/indexmock/mock_Indexer.go | 95 +++ .../index/indexmock/mock_MultiIndexer.go | 159 +++++ .../index/indexmock/mock_ResourceIterator.go | 78 +++ .../index/indexmock/mock_SingleIndexer.go | 159 +++++ .../index/indexmock/mock_resourceIterable.go | 97 +++ internal/controller/cache/index/interfaces.go | 60 ++ internal/controller/cache/index/iterator.go | 37 + .../controller/cache/index/iterator_test.go | 65 ++ .../testdata/MissingRequiredIndex.golden | 1 + internal/controller/cache/index/txn.go | 173 +++++ internal/controller/cache/index/txn_test.go | 382 +++++++++++ .../controller/cache/indexers/.mockery.yaml | 15 + .../cache/indexers/decoded_indexer.go | 65 ++ .../cache/indexers/decoded_indexer_test.go | 295 ++++++++ .../controller/cache/indexers/id_indexer.go | 48 ++ .../cache/indexers/id_indexer_test.go | 96 +++ .../indexersmock/mock_BoundReferences.go | 122 ++++ .../indexers/indexersmock/mock_FromArgs.go | 95 +++ .../indexersmock/mock_GetSingleRefOrID.go | 80 +++ .../indexersmock/mock_MultiIndexer.go | 97 +++ .../indexersmock/mock_RefOrIDFetcher.go | 80 +++ .../indexersmock/mock_SingleIndexer.go | 97 +++ .../controller/cache/indexers/ref_indexer.go | 37 + .../cache/indexers/ref_indexer_test.go | 106 +++ internal/controller/cache/kind.go | 188 ++++++ internal/controller/cache/kind_test.go | 202 ++++++ .../cache/testdata/CacheTypeError.golden | 1 + .../cache/testdata/DuplicateIndexError.golden | 1 + .../cache/testdata/DuplicateQueryError.golden | 1 + .../cache/testdata/IndexError.golden | 1 + .../cache/testdata/IndexNotFound.golden | 1 + .../cache/testdata/QueryNotFound.golden | 1 + .../cache/testdata/QueryRequired.golden | 1 + internal/controller/controller.go | 450 +++++++------ .../{api_test.go => controller_test.go} | 144 ++-- .../controllermock/mock_CacheIDModifier.go | 95 +++ .../mock_CustomDependencyMapper.go | 93 +++ .../controllermock/mock_DependencyMapper.go | 95 +++ .../mock_DependencyTransform.go | 95 +++ .../controller/controllermock/mock_Lease.go | 116 ++++ .../controllermock/mock_Reconciler.go | 81 +++ .../controller/controllermock/mock_task.go | 78 +++ internal/controller/custom_watch.go | 57 ++ internal/controller/dependencies.go | 2 +- internal/controller/dependency/.mockery.yaml | 11 + internal/controller/dependency/cache..go | 218 ++++++ internal/controller/dependency/cache_test.go | 346 ++++++++++ .../dependencymock/mock_CacheIDModifier.go | 96 +++ .../mock_DependencyTransform.go | 96 +++ .../controller/dependency/higher_order.go | 47 ++ .../dependency/higher_order_test.go | 144 ++++ internal/controller/dependency/simple.go | 54 ++ .../simple_test.go} | 9 +- internal/controller/dependency/transform.go | 74 ++ .../controller/dependency/transform_test.go | 166 +++++ internal/controller/dependency_mappers.go | 69 -- internal/controller/manager.go | 17 +- internal/controller/runner.go | 289 ++++++++ internal/controller/testing.go | 57 ++ internal/controller/watch.go | 42 ++ .../explicitdestinations/controller.go | 7 +- .../meshconfiguration/controller.go | 8 +- .../controllers/meshgateways/controller.go | 8 +- .../proxyconfiguration/controller.go | 7 +- .../internal/controllers/routes/controller.go | 4 +- .../controllers/sidecarproxy/controller.go | 11 +- .../internal/controllers/xds/controller.go | 4 +- .../exportedservices/controller.go | 8 +- internal/resource/demo/controller.go | 7 +- .../internal/generate/generate.go | 4 +- internal/resource/reaper/controller.go | 4 +- internal/resource/resourcetest/builder.go | 5 + .../controllers/namespace/controller.go | 4 +- internal/testing/errors/errors.go | 41 ++ proto-public/go.mod | 1 - proto-public/go.sum | 6 - ...ource_types.gen.go => resources.rtypes.go} | 0 ...ource_types.gen.go => resources.rtypes.go} | 0 proto-public/pbdns/mock_DNSServiceClient.go | 64 -- proto-public/pbdns/mock_DNSServiceServer.go | 55 -- .../pbdns/mock_UnsafeDNSServiceServer.go | 30 - ...ource_types.gen.go => resources.rtypes.go} | 0 ...ource_types.gen.go => resources.rtypes.go} | 0 ...ource_types.gen.go => resources.rtypes.go} | 0 proto/buf.gen.yaml | 5 + proto/private/pbdemo/v1/demo.pb.go | 126 ++-- proto/private/pbdemo/v1/demo.proto | 16 +- proto/private/pbdemo/v1/resources.rtypes.go | 50 ++ proto/private/pbdemo/v2/demo.pb.go | 121 ++-- proto/private/pbdemo/v2/demo.proto | 6 + proto/private/pbdemo/v2/resources.rtypes.go | 29 + test/integration/consul-container/go.mod | 2 + test/integration/consul-container/go.sum | 4 + 161 files changed, 14132 insertions(+), 989 deletions(-) create mode 100644 .grpcmocks.yaml create mode 100644 grpcmocks/proto-public/pbacl/mock_ACLServiceClient.go create mode 100644 grpcmocks/proto-public/pbacl/mock_ACLServiceServer.go create mode 100644 grpcmocks/proto-public/pbacl/mock_UnsafeACLServiceServer.go create mode 100644 grpcmocks/proto-public/pbconnectca/mock_ConnectCAServiceClient.go create mode 100644 grpcmocks/proto-public/pbconnectca/mock_ConnectCAServiceServer.go create mode 100644 grpcmocks/proto-public/pbconnectca/mock_ConnectCAService_WatchRootsClient.go create mode 100644 grpcmocks/proto-public/pbconnectca/mock_ConnectCAService_WatchRootsServer.go create mode 100644 grpcmocks/proto-public/pbconnectca/mock_UnsafeConnectCAServiceServer.go create mode 100644 grpcmocks/proto-public/pbdataplane/mock_DataplaneServiceClient.go create mode 100644 grpcmocks/proto-public/pbdataplane/mock_DataplaneServiceServer.go create mode 100644 grpcmocks/proto-public/pbdataplane/mock_UnsafeDataplaneServiceServer.go create mode 100644 grpcmocks/proto-public/pbdataplane/mock_isGetEnvoyBootstrapParamsRequest_NodeSpec.go create mode 100644 grpcmocks/proto-public/pbdns/mock_DNSServiceClient.go create mode 100644 grpcmocks/proto-public/pbdns/mock_DNSServiceServer.go create mode 100644 grpcmocks/proto-public/pbdns/mock_UnsafeDNSServiceServer.go create mode 100644 grpcmocks/proto-public/pbresource/mock_ResourceServiceClient.go create mode 100644 grpcmocks/proto-public/pbresource/mock_ResourceServiceServer.go create mode 100644 grpcmocks/proto-public/pbresource/mock_ResourceService_WatchListClient.go create mode 100644 grpcmocks/proto-public/pbresource/mock_ResourceService_WatchListServer.go create mode 100644 grpcmocks/proto-public/pbresource/mock_UnsafeResourceServiceServer.go create mode 100644 grpcmocks/proto-public/pbserverdiscovery/mock_ServerDiscoveryServiceClient.go create mode 100644 grpcmocks/proto-public/pbserverdiscovery/mock_ServerDiscoveryServiceServer.go create mode 100644 grpcmocks/proto-public/pbserverdiscovery/mock_ServerDiscoveryService_WatchServersClient.go create mode 100644 grpcmocks/proto-public/pbserverdiscovery/mock_ServerDiscoveryService_WatchServersServer.go create mode 100644 grpcmocks/proto-public/pbserverdiscovery/mock_UnsafeServerDiscoveryServiceServer.go create mode 100644 internal/controller/.mockery.yaml delete mode 100644 internal/controller/api.go create mode 100644 internal/controller/cache/.mockery.yaml create mode 100644 internal/controller/cache/cache.go create mode 100644 internal/controller/cache/cache_test.go create mode 100644 internal/controller/cache/cachemock/mock_Cache.go create mode 100644 internal/controller/cache/cachemock/mock_Query.go create mode 100644 internal/controller/cache/cachemock/mock_ReadOnlyCache.go create mode 100644 internal/controller/cache/cachemock/mock_ResourceIterator.go create mode 100644 internal/controller/cache/cachemock/mock_WriteCache.go create mode 100644 internal/controller/cache/client.go create mode 100644 internal/controller/cache/client_test.go create mode 100644 internal/controller/cache/errors.go create mode 100644 internal/controller/cache/errors_test.go create mode 100644 internal/controller/cache/index/.mockery.yaml create mode 100644 internal/controller/cache/index/builder.go create mode 100644 internal/controller/cache/index/builder_test.go create mode 100644 internal/controller/cache/index/convenience.go create mode 100644 internal/controller/cache/index/convenience_test.go create mode 100644 internal/controller/cache/index/errors.go create mode 100644 internal/controller/cache/index/errors_test.go create mode 100644 internal/controller/cache/index/index.go create mode 100644 internal/controller/cache/index/index_test.go create mode 100644 internal/controller/cache/index/indexmock/mock_Indexer.go create mode 100644 internal/controller/cache/index/indexmock/mock_MultiIndexer.go create mode 100644 internal/controller/cache/index/indexmock/mock_ResourceIterator.go create mode 100644 internal/controller/cache/index/indexmock/mock_SingleIndexer.go create mode 100644 internal/controller/cache/index/indexmock/mock_resourceIterable.go create mode 100644 internal/controller/cache/index/interfaces.go create mode 100644 internal/controller/cache/index/iterator.go create mode 100644 internal/controller/cache/index/iterator_test.go create mode 100644 internal/controller/cache/index/testdata/MissingRequiredIndex.golden create mode 100644 internal/controller/cache/index/txn.go create mode 100644 internal/controller/cache/index/txn_test.go create mode 100644 internal/controller/cache/indexers/.mockery.yaml create mode 100644 internal/controller/cache/indexers/decoded_indexer.go create mode 100644 internal/controller/cache/indexers/decoded_indexer_test.go create mode 100644 internal/controller/cache/indexers/id_indexer.go create mode 100644 internal/controller/cache/indexers/id_indexer_test.go create mode 100644 internal/controller/cache/indexers/indexersmock/mock_BoundReferences.go create mode 100644 internal/controller/cache/indexers/indexersmock/mock_FromArgs.go create mode 100644 internal/controller/cache/indexers/indexersmock/mock_GetSingleRefOrID.go create mode 100644 internal/controller/cache/indexers/indexersmock/mock_MultiIndexer.go create mode 100644 internal/controller/cache/indexers/indexersmock/mock_RefOrIDFetcher.go create mode 100644 internal/controller/cache/indexers/indexersmock/mock_SingleIndexer.go create mode 100644 internal/controller/cache/indexers/ref_indexer.go create mode 100644 internal/controller/cache/indexers/ref_indexer_test.go create mode 100644 internal/controller/cache/kind.go create mode 100644 internal/controller/cache/kind_test.go create mode 100644 internal/controller/cache/testdata/CacheTypeError.golden create mode 100644 internal/controller/cache/testdata/DuplicateIndexError.golden create mode 100644 internal/controller/cache/testdata/DuplicateQueryError.golden create mode 100644 internal/controller/cache/testdata/IndexError.golden create mode 100644 internal/controller/cache/testdata/IndexNotFound.golden create mode 100644 internal/controller/cache/testdata/QueryNotFound.golden create mode 100644 internal/controller/cache/testdata/QueryRequired.golden rename internal/controller/{api_test.go => controller_test.go} (68%) create mode 100644 internal/controller/controllermock/mock_CacheIDModifier.go create mode 100644 internal/controller/controllermock/mock_CustomDependencyMapper.go create mode 100644 internal/controller/controllermock/mock_DependencyMapper.go create mode 100644 internal/controller/controllermock/mock_DependencyTransform.go create mode 100644 internal/controller/controllermock/mock_Lease.go create mode 100644 internal/controller/controllermock/mock_Reconciler.go create mode 100644 internal/controller/controllermock/mock_task.go create mode 100644 internal/controller/custom_watch.go create mode 100644 internal/controller/dependency/.mockery.yaml create mode 100644 internal/controller/dependency/cache..go create mode 100644 internal/controller/dependency/cache_test.go create mode 100644 internal/controller/dependency/dependencymock/mock_CacheIDModifier.go create mode 100644 internal/controller/dependency/dependencymock/mock_DependencyTransform.go create mode 100644 internal/controller/dependency/higher_order.go create mode 100644 internal/controller/dependency/higher_order_test.go create mode 100644 internal/controller/dependency/simple.go rename internal/controller/{dependency_mappers_test.go => dependency/simple_test.go} (91%) create mode 100644 internal/controller/dependency/transform.go create mode 100644 internal/controller/dependency/transform_test.go delete mode 100644 internal/controller/dependency_mappers.go create mode 100644 internal/controller/runner.go create mode 100644 internal/controller/testing.go create mode 100644 internal/controller/watch.go create mode 100644 internal/testing/errors/errors.go rename proto-public/pbauth/v2beta1/{resource_types.gen.go => resources.rtypes.go} (100%) rename proto-public/pbcatalog/v2beta1/{resource_types.gen.go => resources.rtypes.go} (100%) delete mode 100644 proto-public/pbdns/mock_DNSServiceClient.go delete mode 100644 proto-public/pbdns/mock_DNSServiceServer.go delete mode 100644 proto-public/pbdns/mock_UnsafeDNSServiceServer.go rename proto-public/pbmesh/v2beta1/{resource_types.gen.go => resources.rtypes.go} (100%) rename proto-public/pbmulticluster/v2beta1/{resource_types.gen.go => resources.rtypes.go} (100%) rename proto-public/pbtenancy/v2beta1/{resource_types.gen.go => resources.rtypes.go} (100%) create mode 100644 proto/private/pbdemo/v1/resources.rtypes.go create mode 100644 proto/private/pbdemo/v2/resources.rtypes.go diff --git a/.grpcmocks.yaml b/.grpcmocks.yaml new file mode 100644 index 0000000000..32067cf10f --- /dev/null +++ b/.grpcmocks.yaml @@ -0,0 +1,20 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +with-expecter: true +all: true +recursive: true +# We don't want the mocks within proto-public to prevent forcing a dependency +# of the testify library on the modules usage. The mocks are only for +# internal testing purposes. Other consumers can generated the mocks into +# their own code base. +dir: "grpcmocks/{{.InterfaceDirRelative}}" +outpkg: "mock{{.PackageName}}" +mockname: "{{.InterfaceName}}" +packages: + github.com/hashicorp/consul/proto-public/pbacl: + github.com/hashicorp/consul/proto-public/pbconnectca: + github.com/hashicorp/consul/proto-public/pbdataplane: + github.com/hashicorp/consul/proto-public/pbserverdiscovery: + github.com/hashicorp/consul/proto-public/pbresource: + github.com/hashicorp/consul/proto-public/pbdns: diff --git a/Makefile b/Makefile index 6f0cc6249c..228c377956 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ GO_MODULES := $(shell find . -name go.mod -exec dirname {} \; | grep -v "proto-g # or the string @DEV to imply use what is currently installed locally. ### GOLANGCI_LINT_VERSION='v1.51.1' -MOCKERY_VERSION='v2.20.0' +MOCKERY_VERSION='v2.37.1' BUF_VERSION='v1.26.0' PROTOC_GEN_GO_GRPC_VERSION='v1.2.0' @@ -562,11 +562,8 @@ proto-gen: proto-tools ## Regenerates all Go files from protobuf definitions .PHONY: proto-mocks proto-mocks: ## Proto mocks - for dir in $(MOCKED_PB_DIRS) ; do \ - cd proto-public && \ - rm -f $$dir/mock*.go && \ - mockery --dir $$dir --inpackage --all --recursive --log-level trace ; \ - done + @rm -rf grpcmocks/* + @mockery --config .grpcmocks.yaml .PHONY: proto-format proto-format: proto-tools ## Proto format diff --git a/agent/grpc-external/services/resource/delete.go b/agent/grpc-external/services/resource/delete.go index 582cc5dbc0..4ea860e2da 100644 --- a/agent/grpc-external/services/resource/delete.go +++ b/agent/grpc-external/services/resource/delete.go @@ -189,7 +189,7 @@ func (s *Server) ensureDeleteRequestValid(req *pbresource.DeleteRequest) (*resou return nil, err } - if err := validateScopedTenancy(reg.Scope, reg.Type, req.Id.Tenancy); err != nil { + if err := validateScopedTenancy(reg.Scope, reg.Type, req.Id.Tenancy, false); err != nil { return nil, err } diff --git a/agent/grpc-external/services/resource/list.go b/agent/grpc-external/services/resource/list.go index dc7f88a404..3fd8b07d13 100644 --- a/agent/grpc-external/services/resource/list.go +++ b/agent/grpc-external/services/resource/list.go @@ -109,7 +109,7 @@ func (s *Server) ensureListRequestValid(req *pbresource.ListRequest) (*resource. } // Error when partition scoped and namespace not empty. - if reg.Scope == resource.ScopePartition && req.Tenancy.Namespace != "" { + if reg.Scope == resource.ScopePartition && req.Tenancy.Namespace != "" && req.Tenancy.Namespace != storage.Wildcard { return nil, status.Errorf( codes.InvalidArgument, "partition scoped type %s cannot have a namespace. got: %s", diff --git a/agent/grpc-external/services/resource/list_by_owner.go b/agent/grpc-external/services/resource/list_by_owner.go index 781f36069c..bb1868a620 100644 --- a/agent/grpc-external/services/resource/list_by_owner.go +++ b/agent/grpc-external/services/resource/list_by_owner.go @@ -104,7 +104,7 @@ func (s *Server) ensureListByOwnerRequestValid(req *pbresource.ListByOwnerReques return nil, err } - if err = validateScopedTenancy(reg.Scope, reg.Type, req.Owner.Tenancy); err != nil { + if err = validateScopedTenancy(reg.Scope, reg.Type, req.Owner.Tenancy, true); err != nil { return nil, err } diff --git a/agent/grpc-external/services/resource/list_test.go b/agent/grpc-external/services/resource/list_test.go index 780497ec0f..efcfa3cafd 100644 --- a/agent/grpc-external/services/resource/list_test.go +++ b/agent/grpc-external/services/resource/list_test.go @@ -220,6 +220,12 @@ func TestList_Tenancy_Defaults_And_Normalization(t *testing.T) { artistRsp, err := client.Write(ctx, &pbresource.WriteRequest{Resource: artist}) require.NoError(t, err) + // Write a cluster scoped Executive + executive, err := demo.GenerateV1Executive("king-arthur", "CEO") + require.NoError(t, err) + executiveRsp, err := client.Write(ctx, &pbresource.WriteRequest{Resource: executive}) + require.NoError(t, err) + // List and verify correct resource returned for empty tenancy units. listRsp, err := client.List(ctx, &pbresource.ListRequest{ Type: tc.typ, @@ -227,10 +233,13 @@ func TestList_Tenancy_Defaults_And_Normalization(t *testing.T) { }) require.NoError(t, err) require.Len(t, listRsp.Resources, 1) - if tc.typ == demo.TypeV1RecordLabel { + switch tc.typ { + case demo.TypeV1RecordLabel: prototest.AssertDeepEqual(t, recordLabelRsp.Resource, listRsp.Resources[0]) - } else { + case demo.TypeV1Artist: prototest.AssertDeepEqual(t, artistRsp.Resource, listRsp.Resources[0]) + case demo.TypeV1Executive: + prototest.AssertDeepEqual(t, executiveRsp.Resource, listRsp.Resources[0]) } }) } diff --git a/agent/grpc-external/services/resource/read.go b/agent/grpc-external/services/resource/read.go index 9fe59024ce..48fa7e4622 100644 --- a/agent/grpc-external/services/resource/read.go +++ b/agent/grpc-external/services/resource/read.go @@ -107,7 +107,7 @@ func (s *Server) ensureReadRequestValid(req *pbresource.ReadRequest) (*resource. } // Check scope - if err = validateScopedTenancy(reg.Scope, req.Id.Type, req.Id.Tenancy); err != nil { + if err = validateScopedTenancy(reg.Scope, req.Id.Type, req.Id.Tenancy, false); err != nil { return nil, err } diff --git a/agent/grpc-external/services/resource/server.go b/agent/grpc-external/services/resource/server.go index b07ebaff38..99af640f84 100644 --- a/agent/grpc-external/services/resource/server.go +++ b/agent/grpc-external/services/resource/server.go @@ -243,8 +243,8 @@ func tenancyExists(reg *resource.Registration, tenancyBridge TenancyBridge, tena return nil } -func validateScopedTenancy(scope resource.Scope, resourceType *pbresource.Type, tenancy *pbresource.Tenancy) error { - if scope == resource.ScopePartition && tenancy.Namespace != "" { +func validateScopedTenancy(scope resource.Scope, resourceType *pbresource.Type, tenancy *pbresource.Tenancy, allowWildcards bool) error { + if scope == resource.ScopePartition && tenancy.Namespace != "" && (!allowWildcards || tenancy.Namespace != storage.Wildcard) { return status.Errorf( codes.InvalidArgument, "partition scoped resource %s cannot have a namespace. got: %s", @@ -252,8 +252,9 @@ func validateScopedTenancy(scope resource.Scope, resourceType *pbresource.Type, tenancy.Namespace, ) } + if scope == resource.ScopeCluster { - if tenancy.Partition != "" { + if tenancy.Partition != "" && (!allowWildcards || tenancy.Partition != storage.Wildcard) { return status.Errorf( codes.InvalidArgument, "cluster scoped resource %s cannot have a partition: %s", @@ -261,7 +262,7 @@ func validateScopedTenancy(scope resource.Scope, resourceType *pbresource.Type, tenancy.Partition, ) } - if tenancy.Namespace != "" { + if tenancy.Namespace != "" && (!allowWildcards || tenancy.Namespace != storage.Wildcard) { return status.Errorf( codes.InvalidArgument, "cluster scoped resource %s cannot have a namespace: %s", diff --git a/agent/grpc-external/services/resource/server_test.go b/agent/grpc-external/services/resource/server_test.go index 6abb4564d8..b072d40bb5 100644 --- a/agent/grpc-external/services/resource/server_test.go +++ b/agent/grpc-external/services/resource/server_test.go @@ -209,6 +209,31 @@ func wildcardTenancyCases() map[string]struct { PeerName: "local", }, }, + "partitioned type with wildcard partition and namespace": { + typ: demo.TypeV1RecordLabel, + tenancy: &pbresource.Tenancy{ + Partition: "*", + Namespace: "*", + PeerName: "local", + }, + }, + "cluster type with empty partition and namespace": { + typ: demo.TypeV1Executive, + tenancy: &pbresource.Tenancy{ + Partition: "", + Namespace: "", + PeerName: "local", + }, + }, + + "cluster type with wildcard partition and namespace": { + typ: demo.TypeV1Executive, + tenancy: &pbresource.Tenancy{ + Partition: "*", + Namespace: "*", + PeerName: "local", + }, + }, } } diff --git a/agent/grpc-external/services/resource/watch.go b/agent/grpc-external/services/resource/watch.go index adabb3d256..7cbd478a80 100644 --- a/agent/grpc-external/services/resource/watch.go +++ b/agent/grpc-external/services/resource/watch.go @@ -116,7 +116,7 @@ func (s *Server) ensureWatchListRequestValid(req *pbresource.WatchListRequest) ( } // Check scope - if err = validateScopedTenancy(reg.Scope, req.Type, req.Tenancy); err != nil { + if err = validateScopedTenancy(reg.Scope, req.Type, req.Tenancy, true); err != nil { return nil, err } diff --git a/agent/grpc-external/services/resource/watch_test.go b/agent/grpc-external/services/resource/watch_test.go index 2c596e01b3..bd37dbef32 100644 --- a/agent/grpc-external/services/resource/watch_test.go +++ b/agent/grpc-external/services/resource/watch_test.go @@ -15,7 +15,6 @@ import ( "github.com/stretchr/testify/require" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "google.golang.org/protobuf/proto" "github.com/hashicorp/consul/acl" svc "github.com/hashicorp/consul/agent/grpc-external/services/resource" @@ -187,24 +186,30 @@ func TestWatchList_Tenancy_Defaults_And_Normalization(t *testing.T) { require.NoError(t, err) rspCh := handleResourceStream(t, stream) - // Testcase will pick one of recordLabel or artist based on scope of type. + // Testcase will pick one of executive, recordLabel or artist based on scope of type. recordLabel, err := demo.GenerateV1RecordLabel("looney-tunes") require.NoError(t, err) artist, err := demo.GenerateV2Artist() require.NoError(t, err) + executive, err := demo.GenerateV1Executive("king-arthur", "CEO") + require.NoError(t, err) // Create and verify upsert event received. rlRsp, err := client.Write(ctx, &pbresource.WriteRequest{Resource: recordLabel}) require.NoError(t, err) artistRsp, err := client.Write(ctx, &pbresource.WriteRequest{Resource: artist}) require.NoError(t, err) + executiveRsp, err := client.Write(ctx, &pbresource.WriteRequest{Resource: executive}) + require.NoError(t, err) var expected *pbresource.Resource switch { - case proto.Equal(tc.typ, demo.TypeV1RecordLabel): + case resource.EqualType(tc.typ, demo.TypeV1RecordLabel): expected = rlRsp.Resource - case proto.Equal(tc.typ, demo.TypeV2Artist): + case resource.EqualType(tc.typ, demo.TypeV2Artist): expected = artistRsp.Resource + case resource.EqualType(tc.typ, demo.TypeV1Executive): + expected = executiveRsp.Resource default: require.Fail(t, "unsupported type", tc.typ) } diff --git a/docs/v2-architecture/controller-architecture/controllers.md b/docs/v2-architecture/controller-architecture/controllers.md index d346dfeacf..60dd74cdf9 100644 --- a/docs/v2-architecture/controller-architecture/controllers.md +++ b/docs/v2-architecture/controller-architecture/controllers.md @@ -17,7 +17,7 @@ A basic controller setup could look like this: ```go func barController() controller.Controller { - return controller.ForType(pbexample.BarType). + return controller.NewController("bar", pbexample.BarType). WithReconciler(barReconciler{}) } ``` @@ -64,7 +64,7 @@ If our resources only have a name-aligned relationship, we can map them with a b ```go func barController() controller.Controller { - return controller.ForType(pbexample.BarType). + return controller.NewController("bar", pbexample.BarType). WithWatch(pbexample.FooType, controller.ReplaceType(pbexample.BarType)). WithReconciler(barReconciler{}) } @@ -94,7 +94,7 @@ func MapOwned(ctx context.Context, rt controller.Runtime, res *pbresource.Resour } func barController() controller.Controller { - return controller.ForType(pbexample.BarType). + return controller.NewController("bar", pbexample.BarType). WithWatch(pbexample.FooType, MapOwned). WithReconciler(barReconciler{}) } @@ -135,67 +135,60 @@ func MapFoo(ctx context.Context, rt controller.Runtime, res *pbresource.Resource This approach is fine for cases when the number of `Bar` resources in a cluster is relatively small. If it's not, then we'd be doing a large `O(N)` search on each `Bar` event which could be too expensive. -#### Caching mappers +#### Caching Mappers For cases when `N` is too large, we'd want to use a caching layer to help us make lookups more efficient so that they don't require an `O(N)` search of potentially all cluster resources. -Caching mappers need to be kept up-to-date by individual controllers and because of their added complexity, it's important -to carefully consider whether these mappers are strictly necessary for any given controller implementation. +The controller runtime contains a controller cache and the facilities to keep the cache up to date in response to watches. Additionally there are dependency mappers provided for querying the cache. -For reference-relationships, we recommend using the `bimapper` to track relationships, while for the workload selector relationships, -we recommend using the `workloadselectionmapper.Mapper` or the underlying `selectiontracker.WorkloadSelectionTracker`. -These two mappers types can be combined into more complex mappers such as the ones used by the `routes-controller` -or the `sidecar-proxy-controller`. +_While it is possible to not use the builtin cache and manage state in dependency mappers yourself, this can get quite complex and reasoning about the correct times to track and untrack relationships is tricky to get right. Usage of the cache is therefore the advised approach._ -In our example, because we `Foo` and `Bar` are using name-reference relationship, we'll use a `bimapper`. +At a high level, the controller author provides the indexes to track for each watchedtype and can then query thosfunc fooFromArgs(args ...any) ([]byte, error)e indexes in the { + +}future. The querying can occur during both dependency mapping and during resource reconciliation. + +The following example shows how to configure the "bar" controller to rereconcile a Bar resource whenever a Foo resource is changed that references the Bar ```go +func fooReferenceFromBar(r *resource.DecodedResource[*pbexample.Bar]) (bool, []byte, error) { + idx := index.IndexFromRefOrID(&pbresource.ID{ + Type: pbexample.FooType, + Tenancy: r.Id.Tenancy, + Name: r.Data.GetFooName(), + }) + + return true, idx, nil +} + func barController() controller.Controller { - mapper := bimapper.New(pbexample.Bar, pbexample.Foo) + fooIndex := indexers.DecodedSingleIndexer( + "foo", + index.ReferenceOrIDFromArgs, + fooReferenceFromBar, + ) - return controller.ForType(pbexample.BarType). - WithWatch(pbexample.FooType, mapper.MapLink). - WithReconciler(barReconciler{mapper: mapper}) + return controller.NewController("bar", pbexample.BarType, fooIndex). + WithWatch( + pbexample.FooType, + dependency.CacheListMapper(pbexample.BarType, fooIndex.Name()), + ). + WithReconciler(barReconciler{}) } ``` -Now we need to make sure that we populate and clear the mapper as necessary. Generally, this should happen when the data -is fetched in the reconcile. +The controller will now reconcile Bar type resources whenever the Foo type resources they reference are updated. No further tracking is necessary as changes to all Bar types will automatically update the cache. -```go -func (b *barReconciler) Reconcile(ctx context.Context, rt Runtime, req Request) error { - // Fetch the `Bar` resource we're reconciling. - barResource, err := resource.GetDecodedResource[*pbexample.Bar](ctx, rt.Client, req.ID) - if err != nil { - return err - } - - // If the resource is not found, we should make sure to untrack it from mapper. - if barResource == nil { - b.mapper.UntrackItem(req.ID) - } - - // Fetch our referenced `Foo` resource. - fooID := &pbresource.ID{ - Type: pbexample.FooType, - Name: barResource.GetData().GetFooName(), - Tenancy: req.Id.Tenancy, - } - res, err := resource.GetDecodedResource[*pbexample.Foo](ctx, rt.Client, fooID) - if err != nil { - return err - } - // If the referenced Foo resource is not found, we should not untrack it in case it comes back. - if res == nil { - // no-op - } - // Otherwise, we need to track it. - b.mapper.TrackItem(req.ID, []resource.ReferenceOrID{fooID}) -} -``` +One limitation of the cache is that it only has knowledge about the current state of resources. That specifically means that the previous state is forgotten once the cache observes a write. This can be problematic when you want to reconcile a resource to no longer take into account something that previously reference it. -TODO: bound ref problem +Lets say there are two types: `Baz` and `ComputedBaz` and a controller that will aggregate all `Baz` resource with some value into a single `ComputedBaz` object. When +a `Baz` resource gets updated to no longer have a value, it should not be represented in the `ComputedBaz` resource. The typical way to work around this is to: + +1. Store references to the resources that were used during reconciliation within the computed/reconciled resource. For types computed by controllers and not expected to be written directly by users a `bound_references` field should be added to the top level resource types message. For other user manageable types the references may need to be stored within the Status field. + +2. Add a cache index to the watch of the computed type (usually the controllers main managed type). This index can use one of the indexers specified within the [`internal/controller/cache/indexers`](../../../internal/controller/cache/indexers/) package. That package contains some builtin functionality around reference indexing. + +3. Update the dependency mappers to query the cache index *in addition to* looking at the current state of the dependent resource. In our example above the `Baz` dependency mapper could use the [`MultiMapper`] to combine querying the cache for `Baz` types that currently should be associated with a `ComputedBaz` and querying the index added in step 2 for previous references. ### Custom Watches diff --git a/docs/v2-architecture/controller-architecture/guide.md b/docs/v2-architecture/controller-architecture/guide.md index 91ae2f494e..969106364e 100644 --- a/docs/v2-architecture/controller-architecture/guide.md +++ b/docs/v2-architecture/controller-architecture/guide.md @@ -251,7 +251,7 @@ import ( ) func barController() controller.Controller { - return controller.ForType(pbv1alpha1.BarType). + return controller.NewController("bar", pbv1alpha1.BarType). WithReconciler(barReconciler{}) } @@ -387,7 +387,7 @@ controller also watches workloads and services. ```Go func barController() controller.Controller { - return controller.ForType(pbv1alpha1.BarType). + return controller.NewController("bar", pbv1alpha1.BarType). WithWatch(pbv1alpha1.BazType, controller.MapOwner) WithReconciler(barReconciler{}) } @@ -397,11 +397,11 @@ The second argument to `WithWatch` is a [dependency mapper] function. Whenever a resource of the watched type is modified, the dependency mapper will be called to determine which of the controller's managed resources need to be reconciled. -[`controller.MapOwner`] is a convenience function which causes the watched +[`dependency.MapOwner`] is a convenience function which causes the watched resource's [owner](#ownership--cascading-deletion) to be reconciled. [dependency mapper]: https://pkg.go.dev/github.com/hashicorp/consul/internal/controller#DependencyMapper -[`controller.MapOwner`]: https://pkg.go.dev/github.com/hashicorp/consul/internal/controller#MapOwner +[`dependency.MapOwner`]: https://pkg.go.dev/github.com/hashicorp/consul/internal/controller/dependency#MapOwner ### Placement @@ -413,7 +413,7 @@ the controller's placement. ```Go func barController() controller.Controller { - return controller.ForType(pbv1alpha1.BarType). + return controller.NewController("bar", pbv1alpha1.BarType). WithPlacement(controller.PlacementEachServer) WithReconciler(barReconciler{}) } diff --git a/go.mod b/go.mod index f5044715ed..358ee2800d 100644 --- a/go.mod +++ b/go.mod @@ -51,6 +51,7 @@ require ( github.com/hashicorp/go-discover v0.0.0-20220714221025-1c234a67149a github.com/hashicorp/go-hclog v1.5.0 github.com/hashicorp/go-immutable-radix v1.3.1 + github.com/hashicorp/go-immutable-radix/v2 v2.1.0 github.com/hashicorp/go-memdb v1.3.4 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-raftchunking v0.7.0 @@ -203,6 +204,7 @@ require ( github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 // indirect github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 // indirect github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.0 // indirect github.com/hashicorp/mdns v1.0.4 // indirect github.com/hashicorp/net-rpc-msgpackrpc/v2 v2.0.0 // indirect github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443 // indirect diff --git a/go.sum b/go.sum index 4f226d0d74..d401feffbe 100644 --- a/go.sum +++ b/go.sum @@ -514,6 +514,8 @@ github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjh github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix/v2 v2.1.0 h1:CUW5RYIcysz+D3B+l1mDeXrQ7fUvGGCwJfdASSzbrfo= +github.com/hashicorp/go-immutable-radix/v2 v2.1.0/go.mod h1:hgdqLXA4f6NIjRVisM1TJ9aOJVNRqKZj+xDGF6m7PBw= github.com/hashicorp/go-kms-wrapping/entropy/v2 v2.0.0/go.mod h1:xvb32K2keAc+R8DSFG2IwDcydK9DBQE+fGA5fsw6hSk= github.com/hashicorp/go-memdb v1.3.4 h1:XSL3NR682X/cVk2IeV0d70N4DZ9ljI885xAEU8IoK3c= github.com/hashicorp/go-memdb v1.3.4/go.mod h1:uBTr1oQbtuMgd1SSGoR8YV27eT3sBHbYiNm53bMpgSg= @@ -567,6 +569,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru/v2 v2.0.0 h1:Lf+9eD8m5pncvHAOCQj49GSN6aQI8XGfI5OpXNkoWaA= +github.com/hashicorp/golang-lru/v2 v2.0.0/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcdiag v0.5.1 h1:KZcx9xzRfEOQ2OMbwPxVvHyXwLLRqYpSHxCEOtHfQ6w= github.com/hashicorp/hcdiag v0.5.1/go.mod h1:RMC2KkffN9uJ+5mFSaL67ZFVj4CDeetPF2d/53XpwXo= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= diff --git a/grpcmocks/proto-public/pbacl/mock_ACLServiceClient.go b/grpcmocks/proto-public/pbacl/mock_ACLServiceClient.go new file mode 100644 index 0000000000..5a32838c7a --- /dev/null +++ b/grpcmocks/proto-public/pbacl/mock_ACLServiceClient.go @@ -0,0 +1,180 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package mockpbacl + +import ( + context "context" + + grpc "google.golang.org/grpc" + + mock "github.com/stretchr/testify/mock" + + pbacl "github.com/hashicorp/consul/proto-public/pbacl" +) + +// ACLServiceClient is an autogenerated mock type for the ACLServiceClient type +type ACLServiceClient struct { + mock.Mock +} + +type ACLServiceClient_Expecter struct { + mock *mock.Mock +} + +func (_m *ACLServiceClient) EXPECT() *ACLServiceClient_Expecter { + return &ACLServiceClient_Expecter{mock: &_m.Mock} +} + +// Login provides a mock function with given fields: ctx, in, opts +func (_m *ACLServiceClient) Login(ctx context.Context, in *pbacl.LoginRequest, opts ...grpc.CallOption) (*pbacl.LoginResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *pbacl.LoginResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *pbacl.LoginRequest, ...grpc.CallOption) (*pbacl.LoginResponse, error)); ok { + return rf(ctx, in, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, *pbacl.LoginRequest, ...grpc.CallOption) *pbacl.LoginResponse); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbacl.LoginResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *pbacl.LoginRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ACLServiceClient_Login_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Login' +type ACLServiceClient_Login_Call struct { + *mock.Call +} + +// Login is a helper method to define mock.On call +// - ctx context.Context +// - in *pbacl.LoginRequest +// - opts ...grpc.CallOption +func (_e *ACLServiceClient_Expecter) Login(ctx interface{}, in interface{}, opts ...interface{}) *ACLServiceClient_Login_Call { + return &ACLServiceClient_Login_Call{Call: _e.mock.On("Login", + append([]interface{}{ctx, in}, opts...)...)} +} + +func (_c *ACLServiceClient_Login_Call) Run(run func(ctx context.Context, in *pbacl.LoginRequest, opts ...grpc.CallOption)) *ACLServiceClient_Login_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]grpc.CallOption, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(grpc.CallOption) + } + } + run(args[0].(context.Context), args[1].(*pbacl.LoginRequest), variadicArgs...) + }) + return _c +} + +func (_c *ACLServiceClient_Login_Call) Return(_a0 *pbacl.LoginResponse, _a1 error) *ACLServiceClient_Login_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ACLServiceClient_Login_Call) RunAndReturn(run func(context.Context, *pbacl.LoginRequest, ...grpc.CallOption) (*pbacl.LoginResponse, error)) *ACLServiceClient_Login_Call { + _c.Call.Return(run) + return _c +} + +// Logout provides a mock function with given fields: ctx, in, opts +func (_m *ACLServiceClient) Logout(ctx context.Context, in *pbacl.LogoutRequest, opts ...grpc.CallOption) (*pbacl.LogoutResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *pbacl.LogoutResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *pbacl.LogoutRequest, ...grpc.CallOption) (*pbacl.LogoutResponse, error)); ok { + return rf(ctx, in, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, *pbacl.LogoutRequest, ...grpc.CallOption) *pbacl.LogoutResponse); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbacl.LogoutResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *pbacl.LogoutRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ACLServiceClient_Logout_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Logout' +type ACLServiceClient_Logout_Call struct { + *mock.Call +} + +// Logout is a helper method to define mock.On call +// - ctx context.Context +// - in *pbacl.LogoutRequest +// - opts ...grpc.CallOption +func (_e *ACLServiceClient_Expecter) Logout(ctx interface{}, in interface{}, opts ...interface{}) *ACLServiceClient_Logout_Call { + return &ACLServiceClient_Logout_Call{Call: _e.mock.On("Logout", + append([]interface{}{ctx, in}, opts...)...)} +} + +func (_c *ACLServiceClient_Logout_Call) Run(run func(ctx context.Context, in *pbacl.LogoutRequest, opts ...grpc.CallOption)) *ACLServiceClient_Logout_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]grpc.CallOption, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(grpc.CallOption) + } + } + run(args[0].(context.Context), args[1].(*pbacl.LogoutRequest), variadicArgs...) + }) + return _c +} + +func (_c *ACLServiceClient_Logout_Call) Return(_a0 *pbacl.LogoutResponse, _a1 error) *ACLServiceClient_Logout_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ACLServiceClient_Logout_Call) RunAndReturn(run func(context.Context, *pbacl.LogoutRequest, ...grpc.CallOption) (*pbacl.LogoutResponse, error)) *ACLServiceClient_Logout_Call { + _c.Call.Return(run) + return _c +} + +// NewACLServiceClient creates a new instance of ACLServiceClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewACLServiceClient(t interface { + mock.TestingT + Cleanup(func()) +}) *ACLServiceClient { + mock := &ACLServiceClient{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/grpcmocks/proto-public/pbacl/mock_ACLServiceServer.go b/grpcmocks/proto-public/pbacl/mock_ACLServiceServer.go new file mode 100644 index 0000000000..0c4ab3c48e --- /dev/null +++ b/grpcmocks/proto-public/pbacl/mock_ACLServiceServer.go @@ -0,0 +1,147 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package mockpbacl + +import ( + context "context" + + pbacl "github.com/hashicorp/consul/proto-public/pbacl" + mock "github.com/stretchr/testify/mock" +) + +// ACLServiceServer is an autogenerated mock type for the ACLServiceServer type +type ACLServiceServer struct { + mock.Mock +} + +type ACLServiceServer_Expecter struct { + mock *mock.Mock +} + +func (_m *ACLServiceServer) EXPECT() *ACLServiceServer_Expecter { + return &ACLServiceServer_Expecter{mock: &_m.Mock} +} + +// Login provides a mock function with given fields: _a0, _a1 +func (_m *ACLServiceServer) Login(_a0 context.Context, _a1 *pbacl.LoginRequest) (*pbacl.LoginResponse, error) { + ret := _m.Called(_a0, _a1) + + var r0 *pbacl.LoginResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *pbacl.LoginRequest) (*pbacl.LoginResponse, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *pbacl.LoginRequest) *pbacl.LoginResponse); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbacl.LoginResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *pbacl.LoginRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ACLServiceServer_Login_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Login' +type ACLServiceServer_Login_Call struct { + *mock.Call +} + +// Login is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 *pbacl.LoginRequest +func (_e *ACLServiceServer_Expecter) Login(_a0 interface{}, _a1 interface{}) *ACLServiceServer_Login_Call { + return &ACLServiceServer_Login_Call{Call: _e.mock.On("Login", _a0, _a1)} +} + +func (_c *ACLServiceServer_Login_Call) Run(run func(_a0 context.Context, _a1 *pbacl.LoginRequest)) *ACLServiceServer_Login_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*pbacl.LoginRequest)) + }) + return _c +} + +func (_c *ACLServiceServer_Login_Call) Return(_a0 *pbacl.LoginResponse, _a1 error) *ACLServiceServer_Login_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ACLServiceServer_Login_Call) RunAndReturn(run func(context.Context, *pbacl.LoginRequest) (*pbacl.LoginResponse, error)) *ACLServiceServer_Login_Call { + _c.Call.Return(run) + return _c +} + +// Logout provides a mock function with given fields: _a0, _a1 +func (_m *ACLServiceServer) Logout(_a0 context.Context, _a1 *pbacl.LogoutRequest) (*pbacl.LogoutResponse, error) { + ret := _m.Called(_a0, _a1) + + var r0 *pbacl.LogoutResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *pbacl.LogoutRequest) (*pbacl.LogoutResponse, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *pbacl.LogoutRequest) *pbacl.LogoutResponse); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbacl.LogoutResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *pbacl.LogoutRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ACLServiceServer_Logout_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Logout' +type ACLServiceServer_Logout_Call struct { + *mock.Call +} + +// Logout is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 *pbacl.LogoutRequest +func (_e *ACLServiceServer_Expecter) Logout(_a0 interface{}, _a1 interface{}) *ACLServiceServer_Logout_Call { + return &ACLServiceServer_Logout_Call{Call: _e.mock.On("Logout", _a0, _a1)} +} + +func (_c *ACLServiceServer_Logout_Call) Run(run func(_a0 context.Context, _a1 *pbacl.LogoutRequest)) *ACLServiceServer_Logout_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*pbacl.LogoutRequest)) + }) + return _c +} + +func (_c *ACLServiceServer_Logout_Call) Return(_a0 *pbacl.LogoutResponse, _a1 error) *ACLServiceServer_Logout_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ACLServiceServer_Logout_Call) RunAndReturn(run func(context.Context, *pbacl.LogoutRequest) (*pbacl.LogoutResponse, error)) *ACLServiceServer_Logout_Call { + _c.Call.Return(run) + return _c +} + +// NewACLServiceServer creates a new instance of ACLServiceServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewACLServiceServer(t interface { + mock.TestingT + Cleanup(func()) +}) *ACLServiceServer { + mock := &ACLServiceServer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/grpcmocks/proto-public/pbacl/mock_UnsafeACLServiceServer.go b/grpcmocks/proto-public/pbacl/mock_UnsafeACLServiceServer.go new file mode 100644 index 0000000000..353dcac269 --- /dev/null +++ b/grpcmocks/proto-public/pbacl/mock_UnsafeACLServiceServer.go @@ -0,0 +1,64 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package mockpbacl + +import mock "github.com/stretchr/testify/mock" + +// UnsafeACLServiceServer is an autogenerated mock type for the UnsafeACLServiceServer type +type UnsafeACLServiceServer struct { + mock.Mock +} + +type UnsafeACLServiceServer_Expecter struct { + mock *mock.Mock +} + +func (_m *UnsafeACLServiceServer) EXPECT() *UnsafeACLServiceServer_Expecter { + return &UnsafeACLServiceServer_Expecter{mock: &_m.Mock} +} + +// mustEmbedUnimplementedACLServiceServer provides a mock function with given fields: +func (_m *UnsafeACLServiceServer) mustEmbedUnimplementedACLServiceServer() { + _m.Called() +} + +// UnsafeACLServiceServer_mustEmbedUnimplementedACLServiceServer_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'mustEmbedUnimplementedACLServiceServer' +type UnsafeACLServiceServer_mustEmbedUnimplementedACLServiceServer_Call struct { + *mock.Call +} + +// mustEmbedUnimplementedACLServiceServer is a helper method to define mock.On call +func (_e *UnsafeACLServiceServer_Expecter) mustEmbedUnimplementedACLServiceServer() *UnsafeACLServiceServer_mustEmbedUnimplementedACLServiceServer_Call { + return &UnsafeACLServiceServer_mustEmbedUnimplementedACLServiceServer_Call{Call: _e.mock.On("mustEmbedUnimplementedACLServiceServer")} +} + +func (_c *UnsafeACLServiceServer_mustEmbedUnimplementedACLServiceServer_Call) Run(run func()) *UnsafeACLServiceServer_mustEmbedUnimplementedACLServiceServer_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UnsafeACLServiceServer_mustEmbedUnimplementedACLServiceServer_Call) Return() *UnsafeACLServiceServer_mustEmbedUnimplementedACLServiceServer_Call { + _c.Call.Return() + return _c +} + +func (_c *UnsafeACLServiceServer_mustEmbedUnimplementedACLServiceServer_Call) RunAndReturn(run func()) *UnsafeACLServiceServer_mustEmbedUnimplementedACLServiceServer_Call { + _c.Call.Return(run) + return _c +} + +// NewUnsafeACLServiceServer creates a new instance of UnsafeACLServiceServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewUnsafeACLServiceServer(t interface { + mock.TestingT + Cleanup(func()) +}) *UnsafeACLServiceServer { + mock := &UnsafeACLServiceServer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/grpcmocks/proto-public/pbconnectca/mock_ConnectCAServiceClient.go b/grpcmocks/proto-public/pbconnectca/mock_ConnectCAServiceClient.go new file mode 100644 index 0000000000..4f5ca789a5 --- /dev/null +++ b/grpcmocks/proto-public/pbconnectca/mock_ConnectCAServiceClient.go @@ -0,0 +1,180 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package mockpbconnectca + +import ( + context "context" + + grpc "google.golang.org/grpc" + + mock "github.com/stretchr/testify/mock" + + pbconnectca "github.com/hashicorp/consul/proto-public/pbconnectca" +) + +// ConnectCAServiceClient is an autogenerated mock type for the ConnectCAServiceClient type +type ConnectCAServiceClient struct { + mock.Mock +} + +type ConnectCAServiceClient_Expecter struct { + mock *mock.Mock +} + +func (_m *ConnectCAServiceClient) EXPECT() *ConnectCAServiceClient_Expecter { + return &ConnectCAServiceClient_Expecter{mock: &_m.Mock} +} + +// Sign provides a mock function with given fields: ctx, in, opts +func (_m *ConnectCAServiceClient) Sign(ctx context.Context, in *pbconnectca.SignRequest, opts ...grpc.CallOption) (*pbconnectca.SignResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *pbconnectca.SignResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *pbconnectca.SignRequest, ...grpc.CallOption) (*pbconnectca.SignResponse, error)); ok { + return rf(ctx, in, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, *pbconnectca.SignRequest, ...grpc.CallOption) *pbconnectca.SignResponse); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbconnectca.SignResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *pbconnectca.SignRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ConnectCAServiceClient_Sign_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Sign' +type ConnectCAServiceClient_Sign_Call struct { + *mock.Call +} + +// Sign is a helper method to define mock.On call +// - ctx context.Context +// - in *pbconnectca.SignRequest +// - opts ...grpc.CallOption +func (_e *ConnectCAServiceClient_Expecter) Sign(ctx interface{}, in interface{}, opts ...interface{}) *ConnectCAServiceClient_Sign_Call { + return &ConnectCAServiceClient_Sign_Call{Call: _e.mock.On("Sign", + append([]interface{}{ctx, in}, opts...)...)} +} + +func (_c *ConnectCAServiceClient_Sign_Call) Run(run func(ctx context.Context, in *pbconnectca.SignRequest, opts ...grpc.CallOption)) *ConnectCAServiceClient_Sign_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]grpc.CallOption, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(grpc.CallOption) + } + } + run(args[0].(context.Context), args[1].(*pbconnectca.SignRequest), variadicArgs...) + }) + return _c +} + +func (_c *ConnectCAServiceClient_Sign_Call) Return(_a0 *pbconnectca.SignResponse, _a1 error) *ConnectCAServiceClient_Sign_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ConnectCAServiceClient_Sign_Call) RunAndReturn(run func(context.Context, *pbconnectca.SignRequest, ...grpc.CallOption) (*pbconnectca.SignResponse, error)) *ConnectCAServiceClient_Sign_Call { + _c.Call.Return(run) + return _c +} + +// WatchRoots provides a mock function with given fields: ctx, in, opts +func (_m *ConnectCAServiceClient) WatchRoots(ctx context.Context, in *pbconnectca.WatchRootsRequest, opts ...grpc.CallOption) (pbconnectca.ConnectCAService_WatchRootsClient, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 pbconnectca.ConnectCAService_WatchRootsClient + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *pbconnectca.WatchRootsRequest, ...grpc.CallOption) (pbconnectca.ConnectCAService_WatchRootsClient, error)); ok { + return rf(ctx, in, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, *pbconnectca.WatchRootsRequest, ...grpc.CallOption) pbconnectca.ConnectCAService_WatchRootsClient); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(pbconnectca.ConnectCAService_WatchRootsClient) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *pbconnectca.WatchRootsRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ConnectCAServiceClient_WatchRoots_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WatchRoots' +type ConnectCAServiceClient_WatchRoots_Call struct { + *mock.Call +} + +// WatchRoots is a helper method to define mock.On call +// - ctx context.Context +// - in *pbconnectca.WatchRootsRequest +// - opts ...grpc.CallOption +func (_e *ConnectCAServiceClient_Expecter) WatchRoots(ctx interface{}, in interface{}, opts ...interface{}) *ConnectCAServiceClient_WatchRoots_Call { + return &ConnectCAServiceClient_WatchRoots_Call{Call: _e.mock.On("WatchRoots", + append([]interface{}{ctx, in}, opts...)...)} +} + +func (_c *ConnectCAServiceClient_WatchRoots_Call) Run(run func(ctx context.Context, in *pbconnectca.WatchRootsRequest, opts ...grpc.CallOption)) *ConnectCAServiceClient_WatchRoots_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]grpc.CallOption, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(grpc.CallOption) + } + } + run(args[0].(context.Context), args[1].(*pbconnectca.WatchRootsRequest), variadicArgs...) + }) + return _c +} + +func (_c *ConnectCAServiceClient_WatchRoots_Call) Return(_a0 pbconnectca.ConnectCAService_WatchRootsClient, _a1 error) *ConnectCAServiceClient_WatchRoots_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ConnectCAServiceClient_WatchRoots_Call) RunAndReturn(run func(context.Context, *pbconnectca.WatchRootsRequest, ...grpc.CallOption) (pbconnectca.ConnectCAService_WatchRootsClient, error)) *ConnectCAServiceClient_WatchRoots_Call { + _c.Call.Return(run) + return _c +} + +// NewConnectCAServiceClient creates a new instance of ConnectCAServiceClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewConnectCAServiceClient(t interface { + mock.TestingT + Cleanup(func()) +}) *ConnectCAServiceClient { + mock := &ConnectCAServiceClient{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/grpcmocks/proto-public/pbconnectca/mock_ConnectCAServiceServer.go b/grpcmocks/proto-public/pbconnectca/mock_ConnectCAServiceServer.go new file mode 100644 index 0000000000..0d2724f8bc --- /dev/null +++ b/grpcmocks/proto-public/pbconnectca/mock_ConnectCAServiceServer.go @@ -0,0 +1,135 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package mockpbconnectca + +import ( + context "context" + + pbconnectca "github.com/hashicorp/consul/proto-public/pbconnectca" + mock "github.com/stretchr/testify/mock" +) + +// ConnectCAServiceServer is an autogenerated mock type for the ConnectCAServiceServer type +type ConnectCAServiceServer struct { + mock.Mock +} + +type ConnectCAServiceServer_Expecter struct { + mock *mock.Mock +} + +func (_m *ConnectCAServiceServer) EXPECT() *ConnectCAServiceServer_Expecter { + return &ConnectCAServiceServer_Expecter{mock: &_m.Mock} +} + +// Sign provides a mock function with given fields: _a0, _a1 +func (_m *ConnectCAServiceServer) Sign(_a0 context.Context, _a1 *pbconnectca.SignRequest) (*pbconnectca.SignResponse, error) { + ret := _m.Called(_a0, _a1) + + var r0 *pbconnectca.SignResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *pbconnectca.SignRequest) (*pbconnectca.SignResponse, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *pbconnectca.SignRequest) *pbconnectca.SignResponse); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbconnectca.SignResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *pbconnectca.SignRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ConnectCAServiceServer_Sign_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Sign' +type ConnectCAServiceServer_Sign_Call struct { + *mock.Call +} + +// Sign is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 *pbconnectca.SignRequest +func (_e *ConnectCAServiceServer_Expecter) Sign(_a0 interface{}, _a1 interface{}) *ConnectCAServiceServer_Sign_Call { + return &ConnectCAServiceServer_Sign_Call{Call: _e.mock.On("Sign", _a0, _a1)} +} + +func (_c *ConnectCAServiceServer_Sign_Call) Run(run func(_a0 context.Context, _a1 *pbconnectca.SignRequest)) *ConnectCAServiceServer_Sign_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*pbconnectca.SignRequest)) + }) + return _c +} + +func (_c *ConnectCAServiceServer_Sign_Call) Return(_a0 *pbconnectca.SignResponse, _a1 error) *ConnectCAServiceServer_Sign_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ConnectCAServiceServer_Sign_Call) RunAndReturn(run func(context.Context, *pbconnectca.SignRequest) (*pbconnectca.SignResponse, error)) *ConnectCAServiceServer_Sign_Call { + _c.Call.Return(run) + return _c +} + +// WatchRoots provides a mock function with given fields: _a0, _a1 +func (_m *ConnectCAServiceServer) WatchRoots(_a0 *pbconnectca.WatchRootsRequest, _a1 pbconnectca.ConnectCAService_WatchRootsServer) error { + ret := _m.Called(_a0, _a1) + + var r0 error + if rf, ok := ret.Get(0).(func(*pbconnectca.WatchRootsRequest, pbconnectca.ConnectCAService_WatchRootsServer) error); ok { + r0 = rf(_a0, _a1) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ConnectCAServiceServer_WatchRoots_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WatchRoots' +type ConnectCAServiceServer_WatchRoots_Call struct { + *mock.Call +} + +// WatchRoots is a helper method to define mock.On call +// - _a0 *pbconnectca.WatchRootsRequest +// - _a1 pbconnectca.ConnectCAService_WatchRootsServer +func (_e *ConnectCAServiceServer_Expecter) WatchRoots(_a0 interface{}, _a1 interface{}) *ConnectCAServiceServer_WatchRoots_Call { + return &ConnectCAServiceServer_WatchRoots_Call{Call: _e.mock.On("WatchRoots", _a0, _a1)} +} + +func (_c *ConnectCAServiceServer_WatchRoots_Call) Run(run func(_a0 *pbconnectca.WatchRootsRequest, _a1 pbconnectca.ConnectCAService_WatchRootsServer)) *ConnectCAServiceServer_WatchRoots_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*pbconnectca.WatchRootsRequest), args[1].(pbconnectca.ConnectCAService_WatchRootsServer)) + }) + return _c +} + +func (_c *ConnectCAServiceServer_WatchRoots_Call) Return(_a0 error) *ConnectCAServiceServer_WatchRoots_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ConnectCAServiceServer_WatchRoots_Call) RunAndReturn(run func(*pbconnectca.WatchRootsRequest, pbconnectca.ConnectCAService_WatchRootsServer) error) *ConnectCAServiceServer_WatchRoots_Call { + _c.Call.Return(run) + return _c +} + +// NewConnectCAServiceServer creates a new instance of ConnectCAServiceServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewConnectCAServiceServer(t interface { + mock.TestingT + Cleanup(func()) +}) *ConnectCAServiceServer { + mock := &ConnectCAServiceServer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/grpcmocks/proto-public/pbconnectca/mock_ConnectCAService_WatchRootsClient.go b/grpcmocks/proto-public/pbconnectca/mock_ConnectCAService_WatchRootsClient.go new file mode 100644 index 0000000000..ec54109509 --- /dev/null +++ b/grpcmocks/proto-public/pbconnectca/mock_ConnectCAService_WatchRootsClient.go @@ -0,0 +1,356 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package mockpbconnectca + +import ( + context "context" + + mock "github.com/stretchr/testify/mock" + metadata "google.golang.org/grpc/metadata" + + pbconnectca "github.com/hashicorp/consul/proto-public/pbconnectca" +) + +// ConnectCAService_WatchRootsClient is an autogenerated mock type for the ConnectCAService_WatchRootsClient type +type ConnectCAService_WatchRootsClient struct { + mock.Mock +} + +type ConnectCAService_WatchRootsClient_Expecter struct { + mock *mock.Mock +} + +func (_m *ConnectCAService_WatchRootsClient) EXPECT() *ConnectCAService_WatchRootsClient_Expecter { + return &ConnectCAService_WatchRootsClient_Expecter{mock: &_m.Mock} +} + +// CloseSend provides a mock function with given fields: +func (_m *ConnectCAService_WatchRootsClient) CloseSend() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ConnectCAService_WatchRootsClient_CloseSend_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CloseSend' +type ConnectCAService_WatchRootsClient_CloseSend_Call struct { + *mock.Call +} + +// CloseSend is a helper method to define mock.On call +func (_e *ConnectCAService_WatchRootsClient_Expecter) CloseSend() *ConnectCAService_WatchRootsClient_CloseSend_Call { + return &ConnectCAService_WatchRootsClient_CloseSend_Call{Call: _e.mock.On("CloseSend")} +} + +func (_c *ConnectCAService_WatchRootsClient_CloseSend_Call) Run(run func()) *ConnectCAService_WatchRootsClient_CloseSend_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ConnectCAService_WatchRootsClient_CloseSend_Call) Return(_a0 error) *ConnectCAService_WatchRootsClient_CloseSend_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ConnectCAService_WatchRootsClient_CloseSend_Call) RunAndReturn(run func() error) *ConnectCAService_WatchRootsClient_CloseSend_Call { + _c.Call.Return(run) + return _c +} + +// Context provides a mock function with given fields: +func (_m *ConnectCAService_WatchRootsClient) Context() context.Context { + ret := _m.Called() + + var r0 context.Context + if rf, ok := ret.Get(0).(func() context.Context); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(context.Context) + } + } + + return r0 +} + +// ConnectCAService_WatchRootsClient_Context_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Context' +type ConnectCAService_WatchRootsClient_Context_Call struct { + *mock.Call +} + +// Context is a helper method to define mock.On call +func (_e *ConnectCAService_WatchRootsClient_Expecter) Context() *ConnectCAService_WatchRootsClient_Context_Call { + return &ConnectCAService_WatchRootsClient_Context_Call{Call: _e.mock.On("Context")} +} + +func (_c *ConnectCAService_WatchRootsClient_Context_Call) Run(run func()) *ConnectCAService_WatchRootsClient_Context_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ConnectCAService_WatchRootsClient_Context_Call) Return(_a0 context.Context) *ConnectCAService_WatchRootsClient_Context_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ConnectCAService_WatchRootsClient_Context_Call) RunAndReturn(run func() context.Context) *ConnectCAService_WatchRootsClient_Context_Call { + _c.Call.Return(run) + return _c +} + +// Header provides a mock function with given fields: +func (_m *ConnectCAService_WatchRootsClient) Header() (metadata.MD, error) { + ret := _m.Called() + + var r0 metadata.MD + var r1 error + if rf, ok := ret.Get(0).(func() (metadata.MD, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() metadata.MD); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(metadata.MD) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ConnectCAService_WatchRootsClient_Header_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Header' +type ConnectCAService_WatchRootsClient_Header_Call struct { + *mock.Call +} + +// Header is a helper method to define mock.On call +func (_e *ConnectCAService_WatchRootsClient_Expecter) Header() *ConnectCAService_WatchRootsClient_Header_Call { + return &ConnectCAService_WatchRootsClient_Header_Call{Call: _e.mock.On("Header")} +} + +func (_c *ConnectCAService_WatchRootsClient_Header_Call) Run(run func()) *ConnectCAService_WatchRootsClient_Header_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ConnectCAService_WatchRootsClient_Header_Call) Return(_a0 metadata.MD, _a1 error) *ConnectCAService_WatchRootsClient_Header_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ConnectCAService_WatchRootsClient_Header_Call) RunAndReturn(run func() (metadata.MD, error)) *ConnectCAService_WatchRootsClient_Header_Call { + _c.Call.Return(run) + return _c +} + +// Recv provides a mock function with given fields: +func (_m *ConnectCAService_WatchRootsClient) Recv() (*pbconnectca.WatchRootsResponse, error) { + ret := _m.Called() + + var r0 *pbconnectca.WatchRootsResponse + var r1 error + if rf, ok := ret.Get(0).(func() (*pbconnectca.WatchRootsResponse, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() *pbconnectca.WatchRootsResponse); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbconnectca.WatchRootsResponse) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ConnectCAService_WatchRootsClient_Recv_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Recv' +type ConnectCAService_WatchRootsClient_Recv_Call struct { + *mock.Call +} + +// Recv is a helper method to define mock.On call +func (_e *ConnectCAService_WatchRootsClient_Expecter) Recv() *ConnectCAService_WatchRootsClient_Recv_Call { + return &ConnectCAService_WatchRootsClient_Recv_Call{Call: _e.mock.On("Recv")} +} + +func (_c *ConnectCAService_WatchRootsClient_Recv_Call) Run(run func()) *ConnectCAService_WatchRootsClient_Recv_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ConnectCAService_WatchRootsClient_Recv_Call) Return(_a0 *pbconnectca.WatchRootsResponse, _a1 error) *ConnectCAService_WatchRootsClient_Recv_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ConnectCAService_WatchRootsClient_Recv_Call) RunAndReturn(run func() (*pbconnectca.WatchRootsResponse, error)) *ConnectCAService_WatchRootsClient_Recv_Call { + _c.Call.Return(run) + return _c +} + +// RecvMsg provides a mock function with given fields: m +func (_m *ConnectCAService_WatchRootsClient) RecvMsg(m interface{}) error { + ret := _m.Called(m) + + var r0 error + if rf, ok := ret.Get(0).(func(interface{}) error); ok { + r0 = rf(m) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ConnectCAService_WatchRootsClient_RecvMsg_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RecvMsg' +type ConnectCAService_WatchRootsClient_RecvMsg_Call struct { + *mock.Call +} + +// RecvMsg is a helper method to define mock.On call +// - m interface{} +func (_e *ConnectCAService_WatchRootsClient_Expecter) RecvMsg(m interface{}) *ConnectCAService_WatchRootsClient_RecvMsg_Call { + return &ConnectCAService_WatchRootsClient_RecvMsg_Call{Call: _e.mock.On("RecvMsg", m)} +} + +func (_c *ConnectCAService_WatchRootsClient_RecvMsg_Call) Run(run func(m interface{})) *ConnectCAService_WatchRootsClient_RecvMsg_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(interface{})) + }) + return _c +} + +func (_c *ConnectCAService_WatchRootsClient_RecvMsg_Call) Return(_a0 error) *ConnectCAService_WatchRootsClient_RecvMsg_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ConnectCAService_WatchRootsClient_RecvMsg_Call) RunAndReturn(run func(interface{}) error) *ConnectCAService_WatchRootsClient_RecvMsg_Call { + _c.Call.Return(run) + return _c +} + +// SendMsg provides a mock function with given fields: m +func (_m *ConnectCAService_WatchRootsClient) SendMsg(m interface{}) error { + ret := _m.Called(m) + + var r0 error + if rf, ok := ret.Get(0).(func(interface{}) error); ok { + r0 = rf(m) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ConnectCAService_WatchRootsClient_SendMsg_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendMsg' +type ConnectCAService_WatchRootsClient_SendMsg_Call struct { + *mock.Call +} + +// SendMsg is a helper method to define mock.On call +// - m interface{} +func (_e *ConnectCAService_WatchRootsClient_Expecter) SendMsg(m interface{}) *ConnectCAService_WatchRootsClient_SendMsg_Call { + return &ConnectCAService_WatchRootsClient_SendMsg_Call{Call: _e.mock.On("SendMsg", m)} +} + +func (_c *ConnectCAService_WatchRootsClient_SendMsg_Call) Run(run func(m interface{})) *ConnectCAService_WatchRootsClient_SendMsg_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(interface{})) + }) + return _c +} + +func (_c *ConnectCAService_WatchRootsClient_SendMsg_Call) Return(_a0 error) *ConnectCAService_WatchRootsClient_SendMsg_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ConnectCAService_WatchRootsClient_SendMsg_Call) RunAndReturn(run func(interface{}) error) *ConnectCAService_WatchRootsClient_SendMsg_Call { + _c.Call.Return(run) + return _c +} + +// Trailer provides a mock function with given fields: +func (_m *ConnectCAService_WatchRootsClient) Trailer() metadata.MD { + ret := _m.Called() + + var r0 metadata.MD + if rf, ok := ret.Get(0).(func() metadata.MD); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(metadata.MD) + } + } + + return r0 +} + +// ConnectCAService_WatchRootsClient_Trailer_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Trailer' +type ConnectCAService_WatchRootsClient_Trailer_Call struct { + *mock.Call +} + +// Trailer is a helper method to define mock.On call +func (_e *ConnectCAService_WatchRootsClient_Expecter) Trailer() *ConnectCAService_WatchRootsClient_Trailer_Call { + return &ConnectCAService_WatchRootsClient_Trailer_Call{Call: _e.mock.On("Trailer")} +} + +func (_c *ConnectCAService_WatchRootsClient_Trailer_Call) Run(run func()) *ConnectCAService_WatchRootsClient_Trailer_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ConnectCAService_WatchRootsClient_Trailer_Call) Return(_a0 metadata.MD) *ConnectCAService_WatchRootsClient_Trailer_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ConnectCAService_WatchRootsClient_Trailer_Call) RunAndReturn(run func() metadata.MD) *ConnectCAService_WatchRootsClient_Trailer_Call { + _c.Call.Return(run) + return _c +} + +// NewConnectCAService_WatchRootsClient creates a new instance of ConnectCAService_WatchRootsClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewConnectCAService_WatchRootsClient(t interface { + mock.TestingT + Cleanup(func()) +}) *ConnectCAService_WatchRootsClient { + mock := &ConnectCAService_WatchRootsClient{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/grpcmocks/proto-public/pbconnectca/mock_ConnectCAService_WatchRootsServer.go b/grpcmocks/proto-public/pbconnectca/mock_ConnectCAService_WatchRootsServer.go new file mode 100644 index 0000000000..f728702161 --- /dev/null +++ b/grpcmocks/proto-public/pbconnectca/mock_ConnectCAService_WatchRootsServer.go @@ -0,0 +1,325 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package mockpbconnectca + +import ( + context "context" + + mock "github.com/stretchr/testify/mock" + metadata "google.golang.org/grpc/metadata" + + pbconnectca "github.com/hashicorp/consul/proto-public/pbconnectca" +) + +// ConnectCAService_WatchRootsServer is an autogenerated mock type for the ConnectCAService_WatchRootsServer type +type ConnectCAService_WatchRootsServer struct { + mock.Mock +} + +type ConnectCAService_WatchRootsServer_Expecter struct { + mock *mock.Mock +} + +func (_m *ConnectCAService_WatchRootsServer) EXPECT() *ConnectCAService_WatchRootsServer_Expecter { + return &ConnectCAService_WatchRootsServer_Expecter{mock: &_m.Mock} +} + +// Context provides a mock function with given fields: +func (_m *ConnectCAService_WatchRootsServer) Context() context.Context { + ret := _m.Called() + + var r0 context.Context + if rf, ok := ret.Get(0).(func() context.Context); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(context.Context) + } + } + + return r0 +} + +// ConnectCAService_WatchRootsServer_Context_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Context' +type ConnectCAService_WatchRootsServer_Context_Call struct { + *mock.Call +} + +// Context is a helper method to define mock.On call +func (_e *ConnectCAService_WatchRootsServer_Expecter) Context() *ConnectCAService_WatchRootsServer_Context_Call { + return &ConnectCAService_WatchRootsServer_Context_Call{Call: _e.mock.On("Context")} +} + +func (_c *ConnectCAService_WatchRootsServer_Context_Call) Run(run func()) *ConnectCAService_WatchRootsServer_Context_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ConnectCAService_WatchRootsServer_Context_Call) Return(_a0 context.Context) *ConnectCAService_WatchRootsServer_Context_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ConnectCAService_WatchRootsServer_Context_Call) RunAndReturn(run func() context.Context) *ConnectCAService_WatchRootsServer_Context_Call { + _c.Call.Return(run) + return _c +} + +// RecvMsg provides a mock function with given fields: m +func (_m *ConnectCAService_WatchRootsServer) RecvMsg(m interface{}) error { + ret := _m.Called(m) + + var r0 error + if rf, ok := ret.Get(0).(func(interface{}) error); ok { + r0 = rf(m) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ConnectCAService_WatchRootsServer_RecvMsg_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RecvMsg' +type ConnectCAService_WatchRootsServer_RecvMsg_Call struct { + *mock.Call +} + +// RecvMsg is a helper method to define mock.On call +// - m interface{} +func (_e *ConnectCAService_WatchRootsServer_Expecter) RecvMsg(m interface{}) *ConnectCAService_WatchRootsServer_RecvMsg_Call { + return &ConnectCAService_WatchRootsServer_RecvMsg_Call{Call: _e.mock.On("RecvMsg", m)} +} + +func (_c *ConnectCAService_WatchRootsServer_RecvMsg_Call) Run(run func(m interface{})) *ConnectCAService_WatchRootsServer_RecvMsg_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(interface{})) + }) + return _c +} + +func (_c *ConnectCAService_WatchRootsServer_RecvMsg_Call) Return(_a0 error) *ConnectCAService_WatchRootsServer_RecvMsg_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ConnectCAService_WatchRootsServer_RecvMsg_Call) RunAndReturn(run func(interface{}) error) *ConnectCAService_WatchRootsServer_RecvMsg_Call { + _c.Call.Return(run) + return _c +} + +// Send provides a mock function with given fields: _a0 +func (_m *ConnectCAService_WatchRootsServer) Send(_a0 *pbconnectca.WatchRootsResponse) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(*pbconnectca.WatchRootsResponse) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ConnectCAService_WatchRootsServer_Send_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Send' +type ConnectCAService_WatchRootsServer_Send_Call struct { + *mock.Call +} + +// Send is a helper method to define mock.On call +// - _a0 *pbconnectca.WatchRootsResponse +func (_e *ConnectCAService_WatchRootsServer_Expecter) Send(_a0 interface{}) *ConnectCAService_WatchRootsServer_Send_Call { + return &ConnectCAService_WatchRootsServer_Send_Call{Call: _e.mock.On("Send", _a0)} +} + +func (_c *ConnectCAService_WatchRootsServer_Send_Call) Run(run func(_a0 *pbconnectca.WatchRootsResponse)) *ConnectCAService_WatchRootsServer_Send_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*pbconnectca.WatchRootsResponse)) + }) + return _c +} + +func (_c *ConnectCAService_WatchRootsServer_Send_Call) Return(_a0 error) *ConnectCAService_WatchRootsServer_Send_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ConnectCAService_WatchRootsServer_Send_Call) RunAndReturn(run func(*pbconnectca.WatchRootsResponse) error) *ConnectCAService_WatchRootsServer_Send_Call { + _c.Call.Return(run) + return _c +} + +// SendHeader provides a mock function with given fields: _a0 +func (_m *ConnectCAService_WatchRootsServer) SendHeader(_a0 metadata.MD) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(metadata.MD) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ConnectCAService_WatchRootsServer_SendHeader_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendHeader' +type ConnectCAService_WatchRootsServer_SendHeader_Call struct { + *mock.Call +} + +// SendHeader is a helper method to define mock.On call +// - _a0 metadata.MD +func (_e *ConnectCAService_WatchRootsServer_Expecter) SendHeader(_a0 interface{}) *ConnectCAService_WatchRootsServer_SendHeader_Call { + return &ConnectCAService_WatchRootsServer_SendHeader_Call{Call: _e.mock.On("SendHeader", _a0)} +} + +func (_c *ConnectCAService_WatchRootsServer_SendHeader_Call) Run(run func(_a0 metadata.MD)) *ConnectCAService_WatchRootsServer_SendHeader_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(metadata.MD)) + }) + return _c +} + +func (_c *ConnectCAService_WatchRootsServer_SendHeader_Call) Return(_a0 error) *ConnectCAService_WatchRootsServer_SendHeader_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ConnectCAService_WatchRootsServer_SendHeader_Call) RunAndReturn(run func(metadata.MD) error) *ConnectCAService_WatchRootsServer_SendHeader_Call { + _c.Call.Return(run) + return _c +} + +// SendMsg provides a mock function with given fields: m +func (_m *ConnectCAService_WatchRootsServer) SendMsg(m interface{}) error { + ret := _m.Called(m) + + var r0 error + if rf, ok := ret.Get(0).(func(interface{}) error); ok { + r0 = rf(m) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ConnectCAService_WatchRootsServer_SendMsg_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendMsg' +type ConnectCAService_WatchRootsServer_SendMsg_Call struct { + *mock.Call +} + +// SendMsg is a helper method to define mock.On call +// - m interface{} +func (_e *ConnectCAService_WatchRootsServer_Expecter) SendMsg(m interface{}) *ConnectCAService_WatchRootsServer_SendMsg_Call { + return &ConnectCAService_WatchRootsServer_SendMsg_Call{Call: _e.mock.On("SendMsg", m)} +} + +func (_c *ConnectCAService_WatchRootsServer_SendMsg_Call) Run(run func(m interface{})) *ConnectCAService_WatchRootsServer_SendMsg_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(interface{})) + }) + return _c +} + +func (_c *ConnectCAService_WatchRootsServer_SendMsg_Call) Return(_a0 error) *ConnectCAService_WatchRootsServer_SendMsg_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ConnectCAService_WatchRootsServer_SendMsg_Call) RunAndReturn(run func(interface{}) error) *ConnectCAService_WatchRootsServer_SendMsg_Call { + _c.Call.Return(run) + return _c +} + +// SetHeader provides a mock function with given fields: _a0 +func (_m *ConnectCAService_WatchRootsServer) SetHeader(_a0 metadata.MD) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(metadata.MD) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ConnectCAService_WatchRootsServer_SetHeader_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetHeader' +type ConnectCAService_WatchRootsServer_SetHeader_Call struct { + *mock.Call +} + +// SetHeader is a helper method to define mock.On call +// - _a0 metadata.MD +func (_e *ConnectCAService_WatchRootsServer_Expecter) SetHeader(_a0 interface{}) *ConnectCAService_WatchRootsServer_SetHeader_Call { + return &ConnectCAService_WatchRootsServer_SetHeader_Call{Call: _e.mock.On("SetHeader", _a0)} +} + +func (_c *ConnectCAService_WatchRootsServer_SetHeader_Call) Run(run func(_a0 metadata.MD)) *ConnectCAService_WatchRootsServer_SetHeader_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(metadata.MD)) + }) + return _c +} + +func (_c *ConnectCAService_WatchRootsServer_SetHeader_Call) Return(_a0 error) *ConnectCAService_WatchRootsServer_SetHeader_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ConnectCAService_WatchRootsServer_SetHeader_Call) RunAndReturn(run func(metadata.MD) error) *ConnectCAService_WatchRootsServer_SetHeader_Call { + _c.Call.Return(run) + return _c +} + +// SetTrailer provides a mock function with given fields: _a0 +func (_m *ConnectCAService_WatchRootsServer) SetTrailer(_a0 metadata.MD) { + _m.Called(_a0) +} + +// ConnectCAService_WatchRootsServer_SetTrailer_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetTrailer' +type ConnectCAService_WatchRootsServer_SetTrailer_Call struct { + *mock.Call +} + +// SetTrailer is a helper method to define mock.On call +// - _a0 metadata.MD +func (_e *ConnectCAService_WatchRootsServer_Expecter) SetTrailer(_a0 interface{}) *ConnectCAService_WatchRootsServer_SetTrailer_Call { + return &ConnectCAService_WatchRootsServer_SetTrailer_Call{Call: _e.mock.On("SetTrailer", _a0)} +} + +func (_c *ConnectCAService_WatchRootsServer_SetTrailer_Call) Run(run func(_a0 metadata.MD)) *ConnectCAService_WatchRootsServer_SetTrailer_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(metadata.MD)) + }) + return _c +} + +func (_c *ConnectCAService_WatchRootsServer_SetTrailer_Call) Return() *ConnectCAService_WatchRootsServer_SetTrailer_Call { + _c.Call.Return() + return _c +} + +func (_c *ConnectCAService_WatchRootsServer_SetTrailer_Call) RunAndReturn(run func(metadata.MD)) *ConnectCAService_WatchRootsServer_SetTrailer_Call { + _c.Call.Return(run) + return _c +} + +// NewConnectCAService_WatchRootsServer creates a new instance of ConnectCAService_WatchRootsServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewConnectCAService_WatchRootsServer(t interface { + mock.TestingT + Cleanup(func()) +}) *ConnectCAService_WatchRootsServer { + mock := &ConnectCAService_WatchRootsServer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/grpcmocks/proto-public/pbconnectca/mock_UnsafeConnectCAServiceServer.go b/grpcmocks/proto-public/pbconnectca/mock_UnsafeConnectCAServiceServer.go new file mode 100644 index 0000000000..4f5e781c78 --- /dev/null +++ b/grpcmocks/proto-public/pbconnectca/mock_UnsafeConnectCAServiceServer.go @@ -0,0 +1,64 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package mockpbconnectca + +import mock "github.com/stretchr/testify/mock" + +// UnsafeConnectCAServiceServer is an autogenerated mock type for the UnsafeConnectCAServiceServer type +type UnsafeConnectCAServiceServer struct { + mock.Mock +} + +type UnsafeConnectCAServiceServer_Expecter struct { + mock *mock.Mock +} + +func (_m *UnsafeConnectCAServiceServer) EXPECT() *UnsafeConnectCAServiceServer_Expecter { + return &UnsafeConnectCAServiceServer_Expecter{mock: &_m.Mock} +} + +// mustEmbedUnimplementedConnectCAServiceServer provides a mock function with given fields: +func (_m *UnsafeConnectCAServiceServer) mustEmbedUnimplementedConnectCAServiceServer() { + _m.Called() +} + +// UnsafeConnectCAServiceServer_mustEmbedUnimplementedConnectCAServiceServer_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'mustEmbedUnimplementedConnectCAServiceServer' +type UnsafeConnectCAServiceServer_mustEmbedUnimplementedConnectCAServiceServer_Call struct { + *mock.Call +} + +// mustEmbedUnimplementedConnectCAServiceServer is a helper method to define mock.On call +func (_e *UnsafeConnectCAServiceServer_Expecter) mustEmbedUnimplementedConnectCAServiceServer() *UnsafeConnectCAServiceServer_mustEmbedUnimplementedConnectCAServiceServer_Call { + return &UnsafeConnectCAServiceServer_mustEmbedUnimplementedConnectCAServiceServer_Call{Call: _e.mock.On("mustEmbedUnimplementedConnectCAServiceServer")} +} + +func (_c *UnsafeConnectCAServiceServer_mustEmbedUnimplementedConnectCAServiceServer_Call) Run(run func()) *UnsafeConnectCAServiceServer_mustEmbedUnimplementedConnectCAServiceServer_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UnsafeConnectCAServiceServer_mustEmbedUnimplementedConnectCAServiceServer_Call) Return() *UnsafeConnectCAServiceServer_mustEmbedUnimplementedConnectCAServiceServer_Call { + _c.Call.Return() + return _c +} + +func (_c *UnsafeConnectCAServiceServer_mustEmbedUnimplementedConnectCAServiceServer_Call) RunAndReturn(run func()) *UnsafeConnectCAServiceServer_mustEmbedUnimplementedConnectCAServiceServer_Call { + _c.Call.Return(run) + return _c +} + +// NewUnsafeConnectCAServiceServer creates a new instance of UnsafeConnectCAServiceServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewUnsafeConnectCAServiceServer(t interface { + mock.TestingT + Cleanup(func()) +}) *UnsafeConnectCAServiceServer { + mock := &UnsafeConnectCAServiceServer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/grpcmocks/proto-public/pbdataplane/mock_DataplaneServiceClient.go b/grpcmocks/proto-public/pbdataplane/mock_DataplaneServiceClient.go new file mode 100644 index 0000000000..272ccbf98a --- /dev/null +++ b/grpcmocks/proto-public/pbdataplane/mock_DataplaneServiceClient.go @@ -0,0 +1,180 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package mockpbdataplane + +import ( + context "context" + + grpc "google.golang.org/grpc" + + mock "github.com/stretchr/testify/mock" + + pbdataplane "github.com/hashicorp/consul/proto-public/pbdataplane" +) + +// DataplaneServiceClient is an autogenerated mock type for the DataplaneServiceClient type +type DataplaneServiceClient struct { + mock.Mock +} + +type DataplaneServiceClient_Expecter struct { + mock *mock.Mock +} + +func (_m *DataplaneServiceClient) EXPECT() *DataplaneServiceClient_Expecter { + return &DataplaneServiceClient_Expecter{mock: &_m.Mock} +} + +// GetEnvoyBootstrapParams provides a mock function with given fields: ctx, in, opts +func (_m *DataplaneServiceClient) GetEnvoyBootstrapParams(ctx context.Context, in *pbdataplane.GetEnvoyBootstrapParamsRequest, opts ...grpc.CallOption) (*pbdataplane.GetEnvoyBootstrapParamsResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *pbdataplane.GetEnvoyBootstrapParamsResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *pbdataplane.GetEnvoyBootstrapParamsRequest, ...grpc.CallOption) (*pbdataplane.GetEnvoyBootstrapParamsResponse, error)); ok { + return rf(ctx, in, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, *pbdataplane.GetEnvoyBootstrapParamsRequest, ...grpc.CallOption) *pbdataplane.GetEnvoyBootstrapParamsResponse); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbdataplane.GetEnvoyBootstrapParamsResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *pbdataplane.GetEnvoyBootstrapParamsRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// DataplaneServiceClient_GetEnvoyBootstrapParams_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetEnvoyBootstrapParams' +type DataplaneServiceClient_GetEnvoyBootstrapParams_Call struct { + *mock.Call +} + +// GetEnvoyBootstrapParams is a helper method to define mock.On call +// - ctx context.Context +// - in *pbdataplane.GetEnvoyBootstrapParamsRequest +// - opts ...grpc.CallOption +func (_e *DataplaneServiceClient_Expecter) GetEnvoyBootstrapParams(ctx interface{}, in interface{}, opts ...interface{}) *DataplaneServiceClient_GetEnvoyBootstrapParams_Call { + return &DataplaneServiceClient_GetEnvoyBootstrapParams_Call{Call: _e.mock.On("GetEnvoyBootstrapParams", + append([]interface{}{ctx, in}, opts...)...)} +} + +func (_c *DataplaneServiceClient_GetEnvoyBootstrapParams_Call) Run(run func(ctx context.Context, in *pbdataplane.GetEnvoyBootstrapParamsRequest, opts ...grpc.CallOption)) *DataplaneServiceClient_GetEnvoyBootstrapParams_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]grpc.CallOption, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(grpc.CallOption) + } + } + run(args[0].(context.Context), args[1].(*pbdataplane.GetEnvoyBootstrapParamsRequest), variadicArgs...) + }) + return _c +} + +func (_c *DataplaneServiceClient_GetEnvoyBootstrapParams_Call) Return(_a0 *pbdataplane.GetEnvoyBootstrapParamsResponse, _a1 error) *DataplaneServiceClient_GetEnvoyBootstrapParams_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *DataplaneServiceClient_GetEnvoyBootstrapParams_Call) RunAndReturn(run func(context.Context, *pbdataplane.GetEnvoyBootstrapParamsRequest, ...grpc.CallOption) (*pbdataplane.GetEnvoyBootstrapParamsResponse, error)) *DataplaneServiceClient_GetEnvoyBootstrapParams_Call { + _c.Call.Return(run) + return _c +} + +// GetSupportedDataplaneFeatures provides a mock function with given fields: ctx, in, opts +func (_m *DataplaneServiceClient) GetSupportedDataplaneFeatures(ctx context.Context, in *pbdataplane.GetSupportedDataplaneFeaturesRequest, opts ...grpc.CallOption) (*pbdataplane.GetSupportedDataplaneFeaturesResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *pbdataplane.GetSupportedDataplaneFeaturesResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *pbdataplane.GetSupportedDataplaneFeaturesRequest, ...grpc.CallOption) (*pbdataplane.GetSupportedDataplaneFeaturesResponse, error)); ok { + return rf(ctx, in, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, *pbdataplane.GetSupportedDataplaneFeaturesRequest, ...grpc.CallOption) *pbdataplane.GetSupportedDataplaneFeaturesResponse); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbdataplane.GetSupportedDataplaneFeaturesResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *pbdataplane.GetSupportedDataplaneFeaturesRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// DataplaneServiceClient_GetSupportedDataplaneFeatures_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetSupportedDataplaneFeatures' +type DataplaneServiceClient_GetSupportedDataplaneFeatures_Call struct { + *mock.Call +} + +// GetSupportedDataplaneFeatures is a helper method to define mock.On call +// - ctx context.Context +// - in *pbdataplane.GetSupportedDataplaneFeaturesRequest +// - opts ...grpc.CallOption +func (_e *DataplaneServiceClient_Expecter) GetSupportedDataplaneFeatures(ctx interface{}, in interface{}, opts ...interface{}) *DataplaneServiceClient_GetSupportedDataplaneFeatures_Call { + return &DataplaneServiceClient_GetSupportedDataplaneFeatures_Call{Call: _e.mock.On("GetSupportedDataplaneFeatures", + append([]interface{}{ctx, in}, opts...)...)} +} + +func (_c *DataplaneServiceClient_GetSupportedDataplaneFeatures_Call) Run(run func(ctx context.Context, in *pbdataplane.GetSupportedDataplaneFeaturesRequest, opts ...grpc.CallOption)) *DataplaneServiceClient_GetSupportedDataplaneFeatures_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]grpc.CallOption, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(grpc.CallOption) + } + } + run(args[0].(context.Context), args[1].(*pbdataplane.GetSupportedDataplaneFeaturesRequest), variadicArgs...) + }) + return _c +} + +func (_c *DataplaneServiceClient_GetSupportedDataplaneFeatures_Call) Return(_a0 *pbdataplane.GetSupportedDataplaneFeaturesResponse, _a1 error) *DataplaneServiceClient_GetSupportedDataplaneFeatures_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *DataplaneServiceClient_GetSupportedDataplaneFeatures_Call) RunAndReturn(run func(context.Context, *pbdataplane.GetSupportedDataplaneFeaturesRequest, ...grpc.CallOption) (*pbdataplane.GetSupportedDataplaneFeaturesResponse, error)) *DataplaneServiceClient_GetSupportedDataplaneFeatures_Call { + _c.Call.Return(run) + return _c +} + +// NewDataplaneServiceClient creates a new instance of DataplaneServiceClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewDataplaneServiceClient(t interface { + mock.TestingT + Cleanup(func()) +}) *DataplaneServiceClient { + mock := &DataplaneServiceClient{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/grpcmocks/proto-public/pbdataplane/mock_DataplaneServiceServer.go b/grpcmocks/proto-public/pbdataplane/mock_DataplaneServiceServer.go new file mode 100644 index 0000000000..473bbd88b9 --- /dev/null +++ b/grpcmocks/proto-public/pbdataplane/mock_DataplaneServiceServer.go @@ -0,0 +1,147 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package mockpbdataplane + +import ( + context "context" + + pbdataplane "github.com/hashicorp/consul/proto-public/pbdataplane" + mock "github.com/stretchr/testify/mock" +) + +// DataplaneServiceServer is an autogenerated mock type for the DataplaneServiceServer type +type DataplaneServiceServer struct { + mock.Mock +} + +type DataplaneServiceServer_Expecter struct { + mock *mock.Mock +} + +func (_m *DataplaneServiceServer) EXPECT() *DataplaneServiceServer_Expecter { + return &DataplaneServiceServer_Expecter{mock: &_m.Mock} +} + +// GetEnvoyBootstrapParams provides a mock function with given fields: _a0, _a1 +func (_m *DataplaneServiceServer) GetEnvoyBootstrapParams(_a0 context.Context, _a1 *pbdataplane.GetEnvoyBootstrapParamsRequest) (*pbdataplane.GetEnvoyBootstrapParamsResponse, error) { + ret := _m.Called(_a0, _a1) + + var r0 *pbdataplane.GetEnvoyBootstrapParamsResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *pbdataplane.GetEnvoyBootstrapParamsRequest) (*pbdataplane.GetEnvoyBootstrapParamsResponse, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *pbdataplane.GetEnvoyBootstrapParamsRequest) *pbdataplane.GetEnvoyBootstrapParamsResponse); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbdataplane.GetEnvoyBootstrapParamsResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *pbdataplane.GetEnvoyBootstrapParamsRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// DataplaneServiceServer_GetEnvoyBootstrapParams_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetEnvoyBootstrapParams' +type DataplaneServiceServer_GetEnvoyBootstrapParams_Call struct { + *mock.Call +} + +// GetEnvoyBootstrapParams is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 *pbdataplane.GetEnvoyBootstrapParamsRequest +func (_e *DataplaneServiceServer_Expecter) GetEnvoyBootstrapParams(_a0 interface{}, _a1 interface{}) *DataplaneServiceServer_GetEnvoyBootstrapParams_Call { + return &DataplaneServiceServer_GetEnvoyBootstrapParams_Call{Call: _e.mock.On("GetEnvoyBootstrapParams", _a0, _a1)} +} + +func (_c *DataplaneServiceServer_GetEnvoyBootstrapParams_Call) Run(run func(_a0 context.Context, _a1 *pbdataplane.GetEnvoyBootstrapParamsRequest)) *DataplaneServiceServer_GetEnvoyBootstrapParams_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*pbdataplane.GetEnvoyBootstrapParamsRequest)) + }) + return _c +} + +func (_c *DataplaneServiceServer_GetEnvoyBootstrapParams_Call) Return(_a0 *pbdataplane.GetEnvoyBootstrapParamsResponse, _a1 error) *DataplaneServiceServer_GetEnvoyBootstrapParams_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *DataplaneServiceServer_GetEnvoyBootstrapParams_Call) RunAndReturn(run func(context.Context, *pbdataplane.GetEnvoyBootstrapParamsRequest) (*pbdataplane.GetEnvoyBootstrapParamsResponse, error)) *DataplaneServiceServer_GetEnvoyBootstrapParams_Call { + _c.Call.Return(run) + return _c +} + +// GetSupportedDataplaneFeatures provides a mock function with given fields: _a0, _a1 +func (_m *DataplaneServiceServer) GetSupportedDataplaneFeatures(_a0 context.Context, _a1 *pbdataplane.GetSupportedDataplaneFeaturesRequest) (*pbdataplane.GetSupportedDataplaneFeaturesResponse, error) { + ret := _m.Called(_a0, _a1) + + var r0 *pbdataplane.GetSupportedDataplaneFeaturesResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *pbdataplane.GetSupportedDataplaneFeaturesRequest) (*pbdataplane.GetSupportedDataplaneFeaturesResponse, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *pbdataplane.GetSupportedDataplaneFeaturesRequest) *pbdataplane.GetSupportedDataplaneFeaturesResponse); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbdataplane.GetSupportedDataplaneFeaturesResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *pbdataplane.GetSupportedDataplaneFeaturesRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// DataplaneServiceServer_GetSupportedDataplaneFeatures_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetSupportedDataplaneFeatures' +type DataplaneServiceServer_GetSupportedDataplaneFeatures_Call struct { + *mock.Call +} + +// GetSupportedDataplaneFeatures is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 *pbdataplane.GetSupportedDataplaneFeaturesRequest +func (_e *DataplaneServiceServer_Expecter) GetSupportedDataplaneFeatures(_a0 interface{}, _a1 interface{}) *DataplaneServiceServer_GetSupportedDataplaneFeatures_Call { + return &DataplaneServiceServer_GetSupportedDataplaneFeatures_Call{Call: _e.mock.On("GetSupportedDataplaneFeatures", _a0, _a1)} +} + +func (_c *DataplaneServiceServer_GetSupportedDataplaneFeatures_Call) Run(run func(_a0 context.Context, _a1 *pbdataplane.GetSupportedDataplaneFeaturesRequest)) *DataplaneServiceServer_GetSupportedDataplaneFeatures_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*pbdataplane.GetSupportedDataplaneFeaturesRequest)) + }) + return _c +} + +func (_c *DataplaneServiceServer_GetSupportedDataplaneFeatures_Call) Return(_a0 *pbdataplane.GetSupportedDataplaneFeaturesResponse, _a1 error) *DataplaneServiceServer_GetSupportedDataplaneFeatures_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *DataplaneServiceServer_GetSupportedDataplaneFeatures_Call) RunAndReturn(run func(context.Context, *pbdataplane.GetSupportedDataplaneFeaturesRequest) (*pbdataplane.GetSupportedDataplaneFeaturesResponse, error)) *DataplaneServiceServer_GetSupportedDataplaneFeatures_Call { + _c.Call.Return(run) + return _c +} + +// NewDataplaneServiceServer creates a new instance of DataplaneServiceServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewDataplaneServiceServer(t interface { + mock.TestingT + Cleanup(func()) +}) *DataplaneServiceServer { + mock := &DataplaneServiceServer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/grpcmocks/proto-public/pbdataplane/mock_UnsafeDataplaneServiceServer.go b/grpcmocks/proto-public/pbdataplane/mock_UnsafeDataplaneServiceServer.go new file mode 100644 index 0000000000..5053b60181 --- /dev/null +++ b/grpcmocks/proto-public/pbdataplane/mock_UnsafeDataplaneServiceServer.go @@ -0,0 +1,64 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package mockpbdataplane + +import mock "github.com/stretchr/testify/mock" + +// UnsafeDataplaneServiceServer is an autogenerated mock type for the UnsafeDataplaneServiceServer type +type UnsafeDataplaneServiceServer struct { + mock.Mock +} + +type UnsafeDataplaneServiceServer_Expecter struct { + mock *mock.Mock +} + +func (_m *UnsafeDataplaneServiceServer) EXPECT() *UnsafeDataplaneServiceServer_Expecter { + return &UnsafeDataplaneServiceServer_Expecter{mock: &_m.Mock} +} + +// mustEmbedUnimplementedDataplaneServiceServer provides a mock function with given fields: +func (_m *UnsafeDataplaneServiceServer) mustEmbedUnimplementedDataplaneServiceServer() { + _m.Called() +} + +// UnsafeDataplaneServiceServer_mustEmbedUnimplementedDataplaneServiceServer_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'mustEmbedUnimplementedDataplaneServiceServer' +type UnsafeDataplaneServiceServer_mustEmbedUnimplementedDataplaneServiceServer_Call struct { + *mock.Call +} + +// mustEmbedUnimplementedDataplaneServiceServer is a helper method to define mock.On call +func (_e *UnsafeDataplaneServiceServer_Expecter) mustEmbedUnimplementedDataplaneServiceServer() *UnsafeDataplaneServiceServer_mustEmbedUnimplementedDataplaneServiceServer_Call { + return &UnsafeDataplaneServiceServer_mustEmbedUnimplementedDataplaneServiceServer_Call{Call: _e.mock.On("mustEmbedUnimplementedDataplaneServiceServer")} +} + +func (_c *UnsafeDataplaneServiceServer_mustEmbedUnimplementedDataplaneServiceServer_Call) Run(run func()) *UnsafeDataplaneServiceServer_mustEmbedUnimplementedDataplaneServiceServer_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UnsafeDataplaneServiceServer_mustEmbedUnimplementedDataplaneServiceServer_Call) Return() *UnsafeDataplaneServiceServer_mustEmbedUnimplementedDataplaneServiceServer_Call { + _c.Call.Return() + return _c +} + +func (_c *UnsafeDataplaneServiceServer_mustEmbedUnimplementedDataplaneServiceServer_Call) RunAndReturn(run func()) *UnsafeDataplaneServiceServer_mustEmbedUnimplementedDataplaneServiceServer_Call { + _c.Call.Return(run) + return _c +} + +// NewUnsafeDataplaneServiceServer creates a new instance of UnsafeDataplaneServiceServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewUnsafeDataplaneServiceServer(t interface { + mock.TestingT + Cleanup(func()) +}) *UnsafeDataplaneServiceServer { + mock := &UnsafeDataplaneServiceServer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/grpcmocks/proto-public/pbdataplane/mock_isGetEnvoyBootstrapParamsRequest_NodeSpec.go b/grpcmocks/proto-public/pbdataplane/mock_isGetEnvoyBootstrapParamsRequest_NodeSpec.go new file mode 100644 index 0000000000..acd420047f --- /dev/null +++ b/grpcmocks/proto-public/pbdataplane/mock_isGetEnvoyBootstrapParamsRequest_NodeSpec.go @@ -0,0 +1,64 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package mockpbdataplane + +import mock "github.com/stretchr/testify/mock" + +// isGetEnvoyBootstrapParamsRequest_NodeSpec is an autogenerated mock type for the isGetEnvoyBootstrapParamsRequest_NodeSpec type +type isGetEnvoyBootstrapParamsRequest_NodeSpec struct { + mock.Mock +} + +type isGetEnvoyBootstrapParamsRequest_NodeSpec_Expecter struct { + mock *mock.Mock +} + +func (_m *isGetEnvoyBootstrapParamsRequest_NodeSpec) EXPECT() *isGetEnvoyBootstrapParamsRequest_NodeSpec_Expecter { + return &isGetEnvoyBootstrapParamsRequest_NodeSpec_Expecter{mock: &_m.Mock} +} + +// isGetEnvoyBootstrapParamsRequest_NodeSpec provides a mock function with given fields: +func (_m *isGetEnvoyBootstrapParamsRequest_NodeSpec) isGetEnvoyBootstrapParamsRequest_NodeSpec() { + _m.Called() +} + +// isGetEnvoyBootstrapParamsRequest_NodeSpec_isGetEnvoyBootstrapParamsRequest_NodeSpec_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'isGetEnvoyBootstrapParamsRequest_NodeSpec' +type isGetEnvoyBootstrapParamsRequest_NodeSpec_isGetEnvoyBootstrapParamsRequest_NodeSpec_Call struct { + *mock.Call +} + +// isGetEnvoyBootstrapParamsRequest_NodeSpec is a helper method to define mock.On call +func (_e *isGetEnvoyBootstrapParamsRequest_NodeSpec_Expecter) isGetEnvoyBootstrapParamsRequest_NodeSpec() *isGetEnvoyBootstrapParamsRequest_NodeSpec_isGetEnvoyBootstrapParamsRequest_NodeSpec_Call { + return &isGetEnvoyBootstrapParamsRequest_NodeSpec_isGetEnvoyBootstrapParamsRequest_NodeSpec_Call{Call: _e.mock.On("isGetEnvoyBootstrapParamsRequest_NodeSpec")} +} + +func (_c *isGetEnvoyBootstrapParamsRequest_NodeSpec_isGetEnvoyBootstrapParamsRequest_NodeSpec_Call) Run(run func()) *isGetEnvoyBootstrapParamsRequest_NodeSpec_isGetEnvoyBootstrapParamsRequest_NodeSpec_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *isGetEnvoyBootstrapParamsRequest_NodeSpec_isGetEnvoyBootstrapParamsRequest_NodeSpec_Call) Return() *isGetEnvoyBootstrapParamsRequest_NodeSpec_isGetEnvoyBootstrapParamsRequest_NodeSpec_Call { + _c.Call.Return() + return _c +} + +func (_c *isGetEnvoyBootstrapParamsRequest_NodeSpec_isGetEnvoyBootstrapParamsRequest_NodeSpec_Call) RunAndReturn(run func()) *isGetEnvoyBootstrapParamsRequest_NodeSpec_isGetEnvoyBootstrapParamsRequest_NodeSpec_Call { + _c.Call.Return(run) + return _c +} + +// newIsGetEnvoyBootstrapParamsRequest_NodeSpec creates a new instance of isGetEnvoyBootstrapParamsRequest_NodeSpec. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func newIsGetEnvoyBootstrapParamsRequest_NodeSpec(t interface { + mock.TestingT + Cleanup(func()) +}) *isGetEnvoyBootstrapParamsRequest_NodeSpec { + mock := &isGetEnvoyBootstrapParamsRequest_NodeSpec{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/grpcmocks/proto-public/pbdns/mock_DNSServiceClient.go b/grpcmocks/proto-public/pbdns/mock_DNSServiceClient.go new file mode 100644 index 0000000000..8da316902a --- /dev/null +++ b/grpcmocks/proto-public/pbdns/mock_DNSServiceClient.go @@ -0,0 +1,110 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package mockpbdns + +import ( + context "context" + + grpc "google.golang.org/grpc" + + mock "github.com/stretchr/testify/mock" + + pbdns "github.com/hashicorp/consul/proto-public/pbdns" +) + +// DNSServiceClient is an autogenerated mock type for the DNSServiceClient type +type DNSServiceClient struct { + mock.Mock +} + +type DNSServiceClient_Expecter struct { + mock *mock.Mock +} + +func (_m *DNSServiceClient) EXPECT() *DNSServiceClient_Expecter { + return &DNSServiceClient_Expecter{mock: &_m.Mock} +} + +// Query provides a mock function with given fields: ctx, in, opts +func (_m *DNSServiceClient) Query(ctx context.Context, in *pbdns.QueryRequest, opts ...grpc.CallOption) (*pbdns.QueryResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *pbdns.QueryResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *pbdns.QueryRequest, ...grpc.CallOption) (*pbdns.QueryResponse, error)); ok { + return rf(ctx, in, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, *pbdns.QueryRequest, ...grpc.CallOption) *pbdns.QueryResponse); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbdns.QueryResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *pbdns.QueryRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// DNSServiceClient_Query_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Query' +type DNSServiceClient_Query_Call struct { + *mock.Call +} + +// Query is a helper method to define mock.On call +// - ctx context.Context +// - in *pbdns.QueryRequest +// - opts ...grpc.CallOption +func (_e *DNSServiceClient_Expecter) Query(ctx interface{}, in interface{}, opts ...interface{}) *DNSServiceClient_Query_Call { + return &DNSServiceClient_Query_Call{Call: _e.mock.On("Query", + append([]interface{}{ctx, in}, opts...)...)} +} + +func (_c *DNSServiceClient_Query_Call) Run(run func(ctx context.Context, in *pbdns.QueryRequest, opts ...grpc.CallOption)) *DNSServiceClient_Query_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]grpc.CallOption, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(grpc.CallOption) + } + } + run(args[0].(context.Context), args[1].(*pbdns.QueryRequest), variadicArgs...) + }) + return _c +} + +func (_c *DNSServiceClient_Query_Call) Return(_a0 *pbdns.QueryResponse, _a1 error) *DNSServiceClient_Query_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *DNSServiceClient_Query_Call) RunAndReturn(run func(context.Context, *pbdns.QueryRequest, ...grpc.CallOption) (*pbdns.QueryResponse, error)) *DNSServiceClient_Query_Call { + _c.Call.Return(run) + return _c +} + +// NewDNSServiceClient creates a new instance of DNSServiceClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewDNSServiceClient(t interface { + mock.TestingT + Cleanup(func()) +}) *DNSServiceClient { + mock := &DNSServiceClient{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/grpcmocks/proto-public/pbdns/mock_DNSServiceServer.go b/grpcmocks/proto-public/pbdns/mock_DNSServiceServer.go new file mode 100644 index 0000000000..f0f70a6364 --- /dev/null +++ b/grpcmocks/proto-public/pbdns/mock_DNSServiceServer.go @@ -0,0 +1,92 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package mockpbdns + +import ( + context "context" + + pbdns "github.com/hashicorp/consul/proto-public/pbdns" + mock "github.com/stretchr/testify/mock" +) + +// DNSServiceServer is an autogenerated mock type for the DNSServiceServer type +type DNSServiceServer struct { + mock.Mock +} + +type DNSServiceServer_Expecter struct { + mock *mock.Mock +} + +func (_m *DNSServiceServer) EXPECT() *DNSServiceServer_Expecter { + return &DNSServiceServer_Expecter{mock: &_m.Mock} +} + +// Query provides a mock function with given fields: _a0, _a1 +func (_m *DNSServiceServer) Query(_a0 context.Context, _a1 *pbdns.QueryRequest) (*pbdns.QueryResponse, error) { + ret := _m.Called(_a0, _a1) + + var r0 *pbdns.QueryResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *pbdns.QueryRequest) (*pbdns.QueryResponse, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *pbdns.QueryRequest) *pbdns.QueryResponse); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbdns.QueryResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *pbdns.QueryRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// DNSServiceServer_Query_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Query' +type DNSServiceServer_Query_Call struct { + *mock.Call +} + +// Query is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 *pbdns.QueryRequest +func (_e *DNSServiceServer_Expecter) Query(_a0 interface{}, _a1 interface{}) *DNSServiceServer_Query_Call { + return &DNSServiceServer_Query_Call{Call: _e.mock.On("Query", _a0, _a1)} +} + +func (_c *DNSServiceServer_Query_Call) Run(run func(_a0 context.Context, _a1 *pbdns.QueryRequest)) *DNSServiceServer_Query_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*pbdns.QueryRequest)) + }) + return _c +} + +func (_c *DNSServiceServer_Query_Call) Return(_a0 *pbdns.QueryResponse, _a1 error) *DNSServiceServer_Query_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *DNSServiceServer_Query_Call) RunAndReturn(run func(context.Context, *pbdns.QueryRequest) (*pbdns.QueryResponse, error)) *DNSServiceServer_Query_Call { + _c.Call.Return(run) + return _c +} + +// NewDNSServiceServer creates a new instance of DNSServiceServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewDNSServiceServer(t interface { + mock.TestingT + Cleanup(func()) +}) *DNSServiceServer { + mock := &DNSServiceServer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/grpcmocks/proto-public/pbdns/mock_UnsafeDNSServiceServer.go b/grpcmocks/proto-public/pbdns/mock_UnsafeDNSServiceServer.go new file mode 100644 index 0000000000..e53efcdfe2 --- /dev/null +++ b/grpcmocks/proto-public/pbdns/mock_UnsafeDNSServiceServer.go @@ -0,0 +1,64 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package mockpbdns + +import mock "github.com/stretchr/testify/mock" + +// UnsafeDNSServiceServer is an autogenerated mock type for the UnsafeDNSServiceServer type +type UnsafeDNSServiceServer struct { + mock.Mock +} + +type UnsafeDNSServiceServer_Expecter struct { + mock *mock.Mock +} + +func (_m *UnsafeDNSServiceServer) EXPECT() *UnsafeDNSServiceServer_Expecter { + return &UnsafeDNSServiceServer_Expecter{mock: &_m.Mock} +} + +// mustEmbedUnimplementedDNSServiceServer provides a mock function with given fields: +func (_m *UnsafeDNSServiceServer) mustEmbedUnimplementedDNSServiceServer() { + _m.Called() +} + +// UnsafeDNSServiceServer_mustEmbedUnimplementedDNSServiceServer_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'mustEmbedUnimplementedDNSServiceServer' +type UnsafeDNSServiceServer_mustEmbedUnimplementedDNSServiceServer_Call struct { + *mock.Call +} + +// mustEmbedUnimplementedDNSServiceServer is a helper method to define mock.On call +func (_e *UnsafeDNSServiceServer_Expecter) mustEmbedUnimplementedDNSServiceServer() *UnsafeDNSServiceServer_mustEmbedUnimplementedDNSServiceServer_Call { + return &UnsafeDNSServiceServer_mustEmbedUnimplementedDNSServiceServer_Call{Call: _e.mock.On("mustEmbedUnimplementedDNSServiceServer")} +} + +func (_c *UnsafeDNSServiceServer_mustEmbedUnimplementedDNSServiceServer_Call) Run(run func()) *UnsafeDNSServiceServer_mustEmbedUnimplementedDNSServiceServer_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UnsafeDNSServiceServer_mustEmbedUnimplementedDNSServiceServer_Call) Return() *UnsafeDNSServiceServer_mustEmbedUnimplementedDNSServiceServer_Call { + _c.Call.Return() + return _c +} + +func (_c *UnsafeDNSServiceServer_mustEmbedUnimplementedDNSServiceServer_Call) RunAndReturn(run func()) *UnsafeDNSServiceServer_mustEmbedUnimplementedDNSServiceServer_Call { + _c.Call.Return(run) + return _c +} + +// NewUnsafeDNSServiceServer creates a new instance of UnsafeDNSServiceServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewUnsafeDNSServiceServer(t interface { + mock.TestingT + Cleanup(func()) +}) *UnsafeDNSServiceServer { + mock := &UnsafeDNSServiceServer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/grpcmocks/proto-public/pbresource/mock_ResourceServiceClient.go b/grpcmocks/proto-public/pbresource/mock_ResourceServiceClient.go new file mode 100644 index 0000000000..c4c0b9c604 --- /dev/null +++ b/grpcmocks/proto-public/pbresource/mock_ResourceServiceClient.go @@ -0,0 +1,530 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package mockpbresource + +import ( + context "context" + + grpc "google.golang.org/grpc" + + mock "github.com/stretchr/testify/mock" + + pbresource "github.com/hashicorp/consul/proto-public/pbresource" +) + +// ResourceServiceClient is an autogenerated mock type for the ResourceServiceClient type +type ResourceServiceClient struct { + mock.Mock +} + +type ResourceServiceClient_Expecter struct { + mock *mock.Mock +} + +func (_m *ResourceServiceClient) EXPECT() *ResourceServiceClient_Expecter { + return &ResourceServiceClient_Expecter{mock: &_m.Mock} +} + +// Delete provides a mock function with given fields: ctx, in, opts +func (_m *ResourceServiceClient) Delete(ctx context.Context, in *pbresource.DeleteRequest, opts ...grpc.CallOption) (*pbresource.DeleteResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *pbresource.DeleteResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *pbresource.DeleteRequest, ...grpc.CallOption) (*pbresource.DeleteResponse, error)); ok { + return rf(ctx, in, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, *pbresource.DeleteRequest, ...grpc.CallOption) *pbresource.DeleteResponse); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbresource.DeleteResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *pbresource.DeleteRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ResourceServiceClient_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete' +type ResourceServiceClient_Delete_Call struct { + *mock.Call +} + +// Delete is a helper method to define mock.On call +// - ctx context.Context +// - in *pbresource.DeleteRequest +// - opts ...grpc.CallOption +func (_e *ResourceServiceClient_Expecter) Delete(ctx interface{}, in interface{}, opts ...interface{}) *ResourceServiceClient_Delete_Call { + return &ResourceServiceClient_Delete_Call{Call: _e.mock.On("Delete", + append([]interface{}{ctx, in}, opts...)...)} +} + +func (_c *ResourceServiceClient_Delete_Call) Run(run func(ctx context.Context, in *pbresource.DeleteRequest, opts ...grpc.CallOption)) *ResourceServiceClient_Delete_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]grpc.CallOption, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(grpc.CallOption) + } + } + run(args[0].(context.Context), args[1].(*pbresource.DeleteRequest), variadicArgs...) + }) + return _c +} + +func (_c *ResourceServiceClient_Delete_Call) Return(_a0 *pbresource.DeleteResponse, _a1 error) *ResourceServiceClient_Delete_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ResourceServiceClient_Delete_Call) RunAndReturn(run func(context.Context, *pbresource.DeleteRequest, ...grpc.CallOption) (*pbresource.DeleteResponse, error)) *ResourceServiceClient_Delete_Call { + _c.Call.Return(run) + return _c +} + +// List provides a mock function with given fields: ctx, in, opts +func (_m *ResourceServiceClient) List(ctx context.Context, in *pbresource.ListRequest, opts ...grpc.CallOption) (*pbresource.ListResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *pbresource.ListResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *pbresource.ListRequest, ...grpc.CallOption) (*pbresource.ListResponse, error)); ok { + return rf(ctx, in, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, *pbresource.ListRequest, ...grpc.CallOption) *pbresource.ListResponse); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbresource.ListResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *pbresource.ListRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ResourceServiceClient_List_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'List' +type ResourceServiceClient_List_Call struct { + *mock.Call +} + +// List is a helper method to define mock.On call +// - ctx context.Context +// - in *pbresource.ListRequest +// - opts ...grpc.CallOption +func (_e *ResourceServiceClient_Expecter) List(ctx interface{}, in interface{}, opts ...interface{}) *ResourceServiceClient_List_Call { + return &ResourceServiceClient_List_Call{Call: _e.mock.On("List", + append([]interface{}{ctx, in}, opts...)...)} +} + +func (_c *ResourceServiceClient_List_Call) Run(run func(ctx context.Context, in *pbresource.ListRequest, opts ...grpc.CallOption)) *ResourceServiceClient_List_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]grpc.CallOption, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(grpc.CallOption) + } + } + run(args[0].(context.Context), args[1].(*pbresource.ListRequest), variadicArgs...) + }) + return _c +} + +func (_c *ResourceServiceClient_List_Call) Return(_a0 *pbresource.ListResponse, _a1 error) *ResourceServiceClient_List_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ResourceServiceClient_List_Call) RunAndReturn(run func(context.Context, *pbresource.ListRequest, ...grpc.CallOption) (*pbresource.ListResponse, error)) *ResourceServiceClient_List_Call { + _c.Call.Return(run) + return _c +} + +// ListByOwner provides a mock function with given fields: ctx, in, opts +func (_m *ResourceServiceClient) ListByOwner(ctx context.Context, in *pbresource.ListByOwnerRequest, opts ...grpc.CallOption) (*pbresource.ListByOwnerResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *pbresource.ListByOwnerResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *pbresource.ListByOwnerRequest, ...grpc.CallOption) (*pbresource.ListByOwnerResponse, error)); ok { + return rf(ctx, in, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, *pbresource.ListByOwnerRequest, ...grpc.CallOption) *pbresource.ListByOwnerResponse); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbresource.ListByOwnerResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *pbresource.ListByOwnerRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ResourceServiceClient_ListByOwner_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListByOwner' +type ResourceServiceClient_ListByOwner_Call struct { + *mock.Call +} + +// ListByOwner is a helper method to define mock.On call +// - ctx context.Context +// - in *pbresource.ListByOwnerRequest +// - opts ...grpc.CallOption +func (_e *ResourceServiceClient_Expecter) ListByOwner(ctx interface{}, in interface{}, opts ...interface{}) *ResourceServiceClient_ListByOwner_Call { + return &ResourceServiceClient_ListByOwner_Call{Call: _e.mock.On("ListByOwner", + append([]interface{}{ctx, in}, opts...)...)} +} + +func (_c *ResourceServiceClient_ListByOwner_Call) Run(run func(ctx context.Context, in *pbresource.ListByOwnerRequest, opts ...grpc.CallOption)) *ResourceServiceClient_ListByOwner_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]grpc.CallOption, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(grpc.CallOption) + } + } + run(args[0].(context.Context), args[1].(*pbresource.ListByOwnerRequest), variadicArgs...) + }) + return _c +} + +func (_c *ResourceServiceClient_ListByOwner_Call) Return(_a0 *pbresource.ListByOwnerResponse, _a1 error) *ResourceServiceClient_ListByOwner_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ResourceServiceClient_ListByOwner_Call) RunAndReturn(run func(context.Context, *pbresource.ListByOwnerRequest, ...grpc.CallOption) (*pbresource.ListByOwnerResponse, error)) *ResourceServiceClient_ListByOwner_Call { + _c.Call.Return(run) + return _c +} + +// Read provides a mock function with given fields: ctx, in, opts +func (_m *ResourceServiceClient) Read(ctx context.Context, in *pbresource.ReadRequest, opts ...grpc.CallOption) (*pbresource.ReadResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *pbresource.ReadResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *pbresource.ReadRequest, ...grpc.CallOption) (*pbresource.ReadResponse, error)); ok { + return rf(ctx, in, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, *pbresource.ReadRequest, ...grpc.CallOption) *pbresource.ReadResponse); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbresource.ReadResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *pbresource.ReadRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ResourceServiceClient_Read_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Read' +type ResourceServiceClient_Read_Call struct { + *mock.Call +} + +// Read is a helper method to define mock.On call +// - ctx context.Context +// - in *pbresource.ReadRequest +// - opts ...grpc.CallOption +func (_e *ResourceServiceClient_Expecter) Read(ctx interface{}, in interface{}, opts ...interface{}) *ResourceServiceClient_Read_Call { + return &ResourceServiceClient_Read_Call{Call: _e.mock.On("Read", + append([]interface{}{ctx, in}, opts...)...)} +} + +func (_c *ResourceServiceClient_Read_Call) Run(run func(ctx context.Context, in *pbresource.ReadRequest, opts ...grpc.CallOption)) *ResourceServiceClient_Read_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]grpc.CallOption, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(grpc.CallOption) + } + } + run(args[0].(context.Context), args[1].(*pbresource.ReadRequest), variadicArgs...) + }) + return _c +} + +func (_c *ResourceServiceClient_Read_Call) Return(_a0 *pbresource.ReadResponse, _a1 error) *ResourceServiceClient_Read_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ResourceServiceClient_Read_Call) RunAndReturn(run func(context.Context, *pbresource.ReadRequest, ...grpc.CallOption) (*pbresource.ReadResponse, error)) *ResourceServiceClient_Read_Call { + _c.Call.Return(run) + return _c +} + +// WatchList provides a mock function with given fields: ctx, in, opts +func (_m *ResourceServiceClient) WatchList(ctx context.Context, in *pbresource.WatchListRequest, opts ...grpc.CallOption) (pbresource.ResourceService_WatchListClient, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 pbresource.ResourceService_WatchListClient + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *pbresource.WatchListRequest, ...grpc.CallOption) (pbresource.ResourceService_WatchListClient, error)); ok { + return rf(ctx, in, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, *pbresource.WatchListRequest, ...grpc.CallOption) pbresource.ResourceService_WatchListClient); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(pbresource.ResourceService_WatchListClient) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *pbresource.WatchListRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ResourceServiceClient_WatchList_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WatchList' +type ResourceServiceClient_WatchList_Call struct { + *mock.Call +} + +// WatchList is a helper method to define mock.On call +// - ctx context.Context +// - in *pbresource.WatchListRequest +// - opts ...grpc.CallOption +func (_e *ResourceServiceClient_Expecter) WatchList(ctx interface{}, in interface{}, opts ...interface{}) *ResourceServiceClient_WatchList_Call { + return &ResourceServiceClient_WatchList_Call{Call: _e.mock.On("WatchList", + append([]interface{}{ctx, in}, opts...)...)} +} + +func (_c *ResourceServiceClient_WatchList_Call) Run(run func(ctx context.Context, in *pbresource.WatchListRequest, opts ...grpc.CallOption)) *ResourceServiceClient_WatchList_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]grpc.CallOption, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(grpc.CallOption) + } + } + run(args[0].(context.Context), args[1].(*pbresource.WatchListRequest), variadicArgs...) + }) + return _c +} + +func (_c *ResourceServiceClient_WatchList_Call) Return(_a0 pbresource.ResourceService_WatchListClient, _a1 error) *ResourceServiceClient_WatchList_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ResourceServiceClient_WatchList_Call) RunAndReturn(run func(context.Context, *pbresource.WatchListRequest, ...grpc.CallOption) (pbresource.ResourceService_WatchListClient, error)) *ResourceServiceClient_WatchList_Call { + _c.Call.Return(run) + return _c +} + +// Write provides a mock function with given fields: ctx, in, opts +func (_m *ResourceServiceClient) Write(ctx context.Context, in *pbresource.WriteRequest, opts ...grpc.CallOption) (*pbresource.WriteResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *pbresource.WriteResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *pbresource.WriteRequest, ...grpc.CallOption) (*pbresource.WriteResponse, error)); ok { + return rf(ctx, in, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, *pbresource.WriteRequest, ...grpc.CallOption) *pbresource.WriteResponse); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbresource.WriteResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *pbresource.WriteRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ResourceServiceClient_Write_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Write' +type ResourceServiceClient_Write_Call struct { + *mock.Call +} + +// Write is a helper method to define mock.On call +// - ctx context.Context +// - in *pbresource.WriteRequest +// - opts ...grpc.CallOption +func (_e *ResourceServiceClient_Expecter) Write(ctx interface{}, in interface{}, opts ...interface{}) *ResourceServiceClient_Write_Call { + return &ResourceServiceClient_Write_Call{Call: _e.mock.On("Write", + append([]interface{}{ctx, in}, opts...)...)} +} + +func (_c *ResourceServiceClient_Write_Call) Run(run func(ctx context.Context, in *pbresource.WriteRequest, opts ...grpc.CallOption)) *ResourceServiceClient_Write_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]grpc.CallOption, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(grpc.CallOption) + } + } + run(args[0].(context.Context), args[1].(*pbresource.WriteRequest), variadicArgs...) + }) + return _c +} + +func (_c *ResourceServiceClient_Write_Call) Return(_a0 *pbresource.WriteResponse, _a1 error) *ResourceServiceClient_Write_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ResourceServiceClient_Write_Call) RunAndReturn(run func(context.Context, *pbresource.WriteRequest, ...grpc.CallOption) (*pbresource.WriteResponse, error)) *ResourceServiceClient_Write_Call { + _c.Call.Return(run) + return _c +} + +// WriteStatus provides a mock function with given fields: ctx, in, opts +func (_m *ResourceServiceClient) WriteStatus(ctx context.Context, in *pbresource.WriteStatusRequest, opts ...grpc.CallOption) (*pbresource.WriteStatusResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *pbresource.WriteStatusResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *pbresource.WriteStatusRequest, ...grpc.CallOption) (*pbresource.WriteStatusResponse, error)); ok { + return rf(ctx, in, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, *pbresource.WriteStatusRequest, ...grpc.CallOption) *pbresource.WriteStatusResponse); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbresource.WriteStatusResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *pbresource.WriteStatusRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ResourceServiceClient_WriteStatus_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WriteStatus' +type ResourceServiceClient_WriteStatus_Call struct { + *mock.Call +} + +// WriteStatus is a helper method to define mock.On call +// - ctx context.Context +// - in *pbresource.WriteStatusRequest +// - opts ...grpc.CallOption +func (_e *ResourceServiceClient_Expecter) WriteStatus(ctx interface{}, in interface{}, opts ...interface{}) *ResourceServiceClient_WriteStatus_Call { + return &ResourceServiceClient_WriteStatus_Call{Call: _e.mock.On("WriteStatus", + append([]interface{}{ctx, in}, opts...)...)} +} + +func (_c *ResourceServiceClient_WriteStatus_Call) Run(run func(ctx context.Context, in *pbresource.WriteStatusRequest, opts ...grpc.CallOption)) *ResourceServiceClient_WriteStatus_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]grpc.CallOption, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(grpc.CallOption) + } + } + run(args[0].(context.Context), args[1].(*pbresource.WriteStatusRequest), variadicArgs...) + }) + return _c +} + +func (_c *ResourceServiceClient_WriteStatus_Call) Return(_a0 *pbresource.WriteStatusResponse, _a1 error) *ResourceServiceClient_WriteStatus_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ResourceServiceClient_WriteStatus_Call) RunAndReturn(run func(context.Context, *pbresource.WriteStatusRequest, ...grpc.CallOption) (*pbresource.WriteStatusResponse, error)) *ResourceServiceClient_WriteStatus_Call { + _c.Call.Return(run) + return _c +} + +// NewResourceServiceClient creates a new instance of ResourceServiceClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewResourceServiceClient(t interface { + mock.TestingT + Cleanup(func()) +}) *ResourceServiceClient { + mock := &ResourceServiceClient{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/grpcmocks/proto-public/pbresource/mock_ResourceServiceServer.go b/grpcmocks/proto-public/pbresource/mock_ResourceServiceServer.go new file mode 100644 index 0000000000..c3040e3500 --- /dev/null +++ b/grpcmocks/proto-public/pbresource/mock_ResourceServiceServer.go @@ -0,0 +1,410 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package mockpbresource + +import ( + context "context" + + pbresource "github.com/hashicorp/consul/proto-public/pbresource" + mock "github.com/stretchr/testify/mock" +) + +// ResourceServiceServer is an autogenerated mock type for the ResourceServiceServer type +type ResourceServiceServer struct { + mock.Mock +} + +type ResourceServiceServer_Expecter struct { + mock *mock.Mock +} + +func (_m *ResourceServiceServer) EXPECT() *ResourceServiceServer_Expecter { + return &ResourceServiceServer_Expecter{mock: &_m.Mock} +} + +// Delete provides a mock function with given fields: _a0, _a1 +func (_m *ResourceServiceServer) Delete(_a0 context.Context, _a1 *pbresource.DeleteRequest) (*pbresource.DeleteResponse, error) { + ret := _m.Called(_a0, _a1) + + var r0 *pbresource.DeleteResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *pbresource.DeleteRequest) (*pbresource.DeleteResponse, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *pbresource.DeleteRequest) *pbresource.DeleteResponse); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbresource.DeleteResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *pbresource.DeleteRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ResourceServiceServer_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete' +type ResourceServiceServer_Delete_Call struct { + *mock.Call +} + +// Delete is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 *pbresource.DeleteRequest +func (_e *ResourceServiceServer_Expecter) Delete(_a0 interface{}, _a1 interface{}) *ResourceServiceServer_Delete_Call { + return &ResourceServiceServer_Delete_Call{Call: _e.mock.On("Delete", _a0, _a1)} +} + +func (_c *ResourceServiceServer_Delete_Call) Run(run func(_a0 context.Context, _a1 *pbresource.DeleteRequest)) *ResourceServiceServer_Delete_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*pbresource.DeleteRequest)) + }) + return _c +} + +func (_c *ResourceServiceServer_Delete_Call) Return(_a0 *pbresource.DeleteResponse, _a1 error) *ResourceServiceServer_Delete_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ResourceServiceServer_Delete_Call) RunAndReturn(run func(context.Context, *pbresource.DeleteRequest) (*pbresource.DeleteResponse, error)) *ResourceServiceServer_Delete_Call { + _c.Call.Return(run) + return _c +} + +// List provides a mock function with given fields: _a0, _a1 +func (_m *ResourceServiceServer) List(_a0 context.Context, _a1 *pbresource.ListRequest) (*pbresource.ListResponse, error) { + ret := _m.Called(_a0, _a1) + + var r0 *pbresource.ListResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *pbresource.ListRequest) (*pbresource.ListResponse, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *pbresource.ListRequest) *pbresource.ListResponse); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbresource.ListResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *pbresource.ListRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ResourceServiceServer_List_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'List' +type ResourceServiceServer_List_Call struct { + *mock.Call +} + +// List is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 *pbresource.ListRequest +func (_e *ResourceServiceServer_Expecter) List(_a0 interface{}, _a1 interface{}) *ResourceServiceServer_List_Call { + return &ResourceServiceServer_List_Call{Call: _e.mock.On("List", _a0, _a1)} +} + +func (_c *ResourceServiceServer_List_Call) Run(run func(_a0 context.Context, _a1 *pbresource.ListRequest)) *ResourceServiceServer_List_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*pbresource.ListRequest)) + }) + return _c +} + +func (_c *ResourceServiceServer_List_Call) Return(_a0 *pbresource.ListResponse, _a1 error) *ResourceServiceServer_List_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ResourceServiceServer_List_Call) RunAndReturn(run func(context.Context, *pbresource.ListRequest) (*pbresource.ListResponse, error)) *ResourceServiceServer_List_Call { + _c.Call.Return(run) + return _c +} + +// ListByOwner provides a mock function with given fields: _a0, _a1 +func (_m *ResourceServiceServer) ListByOwner(_a0 context.Context, _a1 *pbresource.ListByOwnerRequest) (*pbresource.ListByOwnerResponse, error) { + ret := _m.Called(_a0, _a1) + + var r0 *pbresource.ListByOwnerResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *pbresource.ListByOwnerRequest) (*pbresource.ListByOwnerResponse, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *pbresource.ListByOwnerRequest) *pbresource.ListByOwnerResponse); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbresource.ListByOwnerResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *pbresource.ListByOwnerRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ResourceServiceServer_ListByOwner_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListByOwner' +type ResourceServiceServer_ListByOwner_Call struct { + *mock.Call +} + +// ListByOwner is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 *pbresource.ListByOwnerRequest +func (_e *ResourceServiceServer_Expecter) ListByOwner(_a0 interface{}, _a1 interface{}) *ResourceServiceServer_ListByOwner_Call { + return &ResourceServiceServer_ListByOwner_Call{Call: _e.mock.On("ListByOwner", _a0, _a1)} +} + +func (_c *ResourceServiceServer_ListByOwner_Call) Run(run func(_a0 context.Context, _a1 *pbresource.ListByOwnerRequest)) *ResourceServiceServer_ListByOwner_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*pbresource.ListByOwnerRequest)) + }) + return _c +} + +func (_c *ResourceServiceServer_ListByOwner_Call) Return(_a0 *pbresource.ListByOwnerResponse, _a1 error) *ResourceServiceServer_ListByOwner_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ResourceServiceServer_ListByOwner_Call) RunAndReturn(run func(context.Context, *pbresource.ListByOwnerRequest) (*pbresource.ListByOwnerResponse, error)) *ResourceServiceServer_ListByOwner_Call { + _c.Call.Return(run) + return _c +} + +// Read provides a mock function with given fields: _a0, _a1 +func (_m *ResourceServiceServer) Read(_a0 context.Context, _a1 *pbresource.ReadRequest) (*pbresource.ReadResponse, error) { + ret := _m.Called(_a0, _a1) + + var r0 *pbresource.ReadResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *pbresource.ReadRequest) (*pbresource.ReadResponse, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *pbresource.ReadRequest) *pbresource.ReadResponse); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbresource.ReadResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *pbresource.ReadRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ResourceServiceServer_Read_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Read' +type ResourceServiceServer_Read_Call struct { + *mock.Call +} + +// Read is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 *pbresource.ReadRequest +func (_e *ResourceServiceServer_Expecter) Read(_a0 interface{}, _a1 interface{}) *ResourceServiceServer_Read_Call { + return &ResourceServiceServer_Read_Call{Call: _e.mock.On("Read", _a0, _a1)} +} + +func (_c *ResourceServiceServer_Read_Call) Run(run func(_a0 context.Context, _a1 *pbresource.ReadRequest)) *ResourceServiceServer_Read_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*pbresource.ReadRequest)) + }) + return _c +} + +func (_c *ResourceServiceServer_Read_Call) Return(_a0 *pbresource.ReadResponse, _a1 error) *ResourceServiceServer_Read_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ResourceServiceServer_Read_Call) RunAndReturn(run func(context.Context, *pbresource.ReadRequest) (*pbresource.ReadResponse, error)) *ResourceServiceServer_Read_Call { + _c.Call.Return(run) + return _c +} + +// WatchList provides a mock function with given fields: _a0, _a1 +func (_m *ResourceServiceServer) WatchList(_a0 *pbresource.WatchListRequest, _a1 pbresource.ResourceService_WatchListServer) error { + ret := _m.Called(_a0, _a1) + + var r0 error + if rf, ok := ret.Get(0).(func(*pbresource.WatchListRequest, pbresource.ResourceService_WatchListServer) error); ok { + r0 = rf(_a0, _a1) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ResourceServiceServer_WatchList_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WatchList' +type ResourceServiceServer_WatchList_Call struct { + *mock.Call +} + +// WatchList is a helper method to define mock.On call +// - _a0 *pbresource.WatchListRequest +// - _a1 pbresource.ResourceService_WatchListServer +func (_e *ResourceServiceServer_Expecter) WatchList(_a0 interface{}, _a1 interface{}) *ResourceServiceServer_WatchList_Call { + return &ResourceServiceServer_WatchList_Call{Call: _e.mock.On("WatchList", _a0, _a1)} +} + +func (_c *ResourceServiceServer_WatchList_Call) Run(run func(_a0 *pbresource.WatchListRequest, _a1 pbresource.ResourceService_WatchListServer)) *ResourceServiceServer_WatchList_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*pbresource.WatchListRequest), args[1].(pbresource.ResourceService_WatchListServer)) + }) + return _c +} + +func (_c *ResourceServiceServer_WatchList_Call) Return(_a0 error) *ResourceServiceServer_WatchList_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ResourceServiceServer_WatchList_Call) RunAndReturn(run func(*pbresource.WatchListRequest, pbresource.ResourceService_WatchListServer) error) *ResourceServiceServer_WatchList_Call { + _c.Call.Return(run) + return _c +} + +// Write provides a mock function with given fields: _a0, _a1 +func (_m *ResourceServiceServer) Write(_a0 context.Context, _a1 *pbresource.WriteRequest) (*pbresource.WriteResponse, error) { + ret := _m.Called(_a0, _a1) + + var r0 *pbresource.WriteResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *pbresource.WriteRequest) (*pbresource.WriteResponse, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *pbresource.WriteRequest) *pbresource.WriteResponse); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbresource.WriteResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *pbresource.WriteRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ResourceServiceServer_Write_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Write' +type ResourceServiceServer_Write_Call struct { + *mock.Call +} + +// Write is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 *pbresource.WriteRequest +func (_e *ResourceServiceServer_Expecter) Write(_a0 interface{}, _a1 interface{}) *ResourceServiceServer_Write_Call { + return &ResourceServiceServer_Write_Call{Call: _e.mock.On("Write", _a0, _a1)} +} + +func (_c *ResourceServiceServer_Write_Call) Run(run func(_a0 context.Context, _a1 *pbresource.WriteRequest)) *ResourceServiceServer_Write_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*pbresource.WriteRequest)) + }) + return _c +} + +func (_c *ResourceServiceServer_Write_Call) Return(_a0 *pbresource.WriteResponse, _a1 error) *ResourceServiceServer_Write_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ResourceServiceServer_Write_Call) RunAndReturn(run func(context.Context, *pbresource.WriteRequest) (*pbresource.WriteResponse, error)) *ResourceServiceServer_Write_Call { + _c.Call.Return(run) + return _c +} + +// WriteStatus provides a mock function with given fields: _a0, _a1 +func (_m *ResourceServiceServer) WriteStatus(_a0 context.Context, _a1 *pbresource.WriteStatusRequest) (*pbresource.WriteStatusResponse, error) { + ret := _m.Called(_a0, _a1) + + var r0 *pbresource.WriteStatusResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *pbresource.WriteStatusRequest) (*pbresource.WriteStatusResponse, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *pbresource.WriteStatusRequest) *pbresource.WriteStatusResponse); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbresource.WriteStatusResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *pbresource.WriteStatusRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ResourceServiceServer_WriteStatus_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WriteStatus' +type ResourceServiceServer_WriteStatus_Call struct { + *mock.Call +} + +// WriteStatus is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 *pbresource.WriteStatusRequest +func (_e *ResourceServiceServer_Expecter) WriteStatus(_a0 interface{}, _a1 interface{}) *ResourceServiceServer_WriteStatus_Call { + return &ResourceServiceServer_WriteStatus_Call{Call: _e.mock.On("WriteStatus", _a0, _a1)} +} + +func (_c *ResourceServiceServer_WriteStatus_Call) Run(run func(_a0 context.Context, _a1 *pbresource.WriteStatusRequest)) *ResourceServiceServer_WriteStatus_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*pbresource.WriteStatusRequest)) + }) + return _c +} + +func (_c *ResourceServiceServer_WriteStatus_Call) Return(_a0 *pbresource.WriteStatusResponse, _a1 error) *ResourceServiceServer_WriteStatus_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ResourceServiceServer_WriteStatus_Call) RunAndReturn(run func(context.Context, *pbresource.WriteStatusRequest) (*pbresource.WriteStatusResponse, error)) *ResourceServiceServer_WriteStatus_Call { + _c.Call.Return(run) + return _c +} + +// NewResourceServiceServer creates a new instance of ResourceServiceServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewResourceServiceServer(t interface { + mock.TestingT + Cleanup(func()) +}) *ResourceServiceServer { + mock := &ResourceServiceServer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/grpcmocks/proto-public/pbresource/mock_ResourceService_WatchListClient.go b/grpcmocks/proto-public/pbresource/mock_ResourceService_WatchListClient.go new file mode 100644 index 0000000000..b6fb41b073 --- /dev/null +++ b/grpcmocks/proto-public/pbresource/mock_ResourceService_WatchListClient.go @@ -0,0 +1,356 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package mockpbresource + +import ( + context "context" + + mock "github.com/stretchr/testify/mock" + metadata "google.golang.org/grpc/metadata" + + pbresource "github.com/hashicorp/consul/proto-public/pbresource" +) + +// ResourceService_WatchListClient is an autogenerated mock type for the ResourceService_WatchListClient type +type ResourceService_WatchListClient struct { + mock.Mock +} + +type ResourceService_WatchListClient_Expecter struct { + mock *mock.Mock +} + +func (_m *ResourceService_WatchListClient) EXPECT() *ResourceService_WatchListClient_Expecter { + return &ResourceService_WatchListClient_Expecter{mock: &_m.Mock} +} + +// CloseSend provides a mock function with given fields: +func (_m *ResourceService_WatchListClient) CloseSend() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ResourceService_WatchListClient_CloseSend_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CloseSend' +type ResourceService_WatchListClient_CloseSend_Call struct { + *mock.Call +} + +// CloseSend is a helper method to define mock.On call +func (_e *ResourceService_WatchListClient_Expecter) CloseSend() *ResourceService_WatchListClient_CloseSend_Call { + return &ResourceService_WatchListClient_CloseSend_Call{Call: _e.mock.On("CloseSend")} +} + +func (_c *ResourceService_WatchListClient_CloseSend_Call) Run(run func()) *ResourceService_WatchListClient_CloseSend_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ResourceService_WatchListClient_CloseSend_Call) Return(_a0 error) *ResourceService_WatchListClient_CloseSend_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ResourceService_WatchListClient_CloseSend_Call) RunAndReturn(run func() error) *ResourceService_WatchListClient_CloseSend_Call { + _c.Call.Return(run) + return _c +} + +// Context provides a mock function with given fields: +func (_m *ResourceService_WatchListClient) Context() context.Context { + ret := _m.Called() + + var r0 context.Context + if rf, ok := ret.Get(0).(func() context.Context); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(context.Context) + } + } + + return r0 +} + +// ResourceService_WatchListClient_Context_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Context' +type ResourceService_WatchListClient_Context_Call struct { + *mock.Call +} + +// Context is a helper method to define mock.On call +func (_e *ResourceService_WatchListClient_Expecter) Context() *ResourceService_WatchListClient_Context_Call { + return &ResourceService_WatchListClient_Context_Call{Call: _e.mock.On("Context")} +} + +func (_c *ResourceService_WatchListClient_Context_Call) Run(run func()) *ResourceService_WatchListClient_Context_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ResourceService_WatchListClient_Context_Call) Return(_a0 context.Context) *ResourceService_WatchListClient_Context_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ResourceService_WatchListClient_Context_Call) RunAndReturn(run func() context.Context) *ResourceService_WatchListClient_Context_Call { + _c.Call.Return(run) + return _c +} + +// Header provides a mock function with given fields: +func (_m *ResourceService_WatchListClient) Header() (metadata.MD, error) { + ret := _m.Called() + + var r0 metadata.MD + var r1 error + if rf, ok := ret.Get(0).(func() (metadata.MD, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() metadata.MD); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(metadata.MD) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ResourceService_WatchListClient_Header_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Header' +type ResourceService_WatchListClient_Header_Call struct { + *mock.Call +} + +// Header is a helper method to define mock.On call +func (_e *ResourceService_WatchListClient_Expecter) Header() *ResourceService_WatchListClient_Header_Call { + return &ResourceService_WatchListClient_Header_Call{Call: _e.mock.On("Header")} +} + +func (_c *ResourceService_WatchListClient_Header_Call) Run(run func()) *ResourceService_WatchListClient_Header_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ResourceService_WatchListClient_Header_Call) Return(_a0 metadata.MD, _a1 error) *ResourceService_WatchListClient_Header_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ResourceService_WatchListClient_Header_Call) RunAndReturn(run func() (metadata.MD, error)) *ResourceService_WatchListClient_Header_Call { + _c.Call.Return(run) + return _c +} + +// Recv provides a mock function with given fields: +func (_m *ResourceService_WatchListClient) Recv() (*pbresource.WatchEvent, error) { + ret := _m.Called() + + var r0 *pbresource.WatchEvent + var r1 error + if rf, ok := ret.Get(0).(func() (*pbresource.WatchEvent, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() *pbresource.WatchEvent); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbresource.WatchEvent) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ResourceService_WatchListClient_Recv_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Recv' +type ResourceService_WatchListClient_Recv_Call struct { + *mock.Call +} + +// Recv is a helper method to define mock.On call +func (_e *ResourceService_WatchListClient_Expecter) Recv() *ResourceService_WatchListClient_Recv_Call { + return &ResourceService_WatchListClient_Recv_Call{Call: _e.mock.On("Recv")} +} + +func (_c *ResourceService_WatchListClient_Recv_Call) Run(run func()) *ResourceService_WatchListClient_Recv_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ResourceService_WatchListClient_Recv_Call) Return(_a0 *pbresource.WatchEvent, _a1 error) *ResourceService_WatchListClient_Recv_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ResourceService_WatchListClient_Recv_Call) RunAndReturn(run func() (*pbresource.WatchEvent, error)) *ResourceService_WatchListClient_Recv_Call { + _c.Call.Return(run) + return _c +} + +// RecvMsg provides a mock function with given fields: m +func (_m *ResourceService_WatchListClient) RecvMsg(m interface{}) error { + ret := _m.Called(m) + + var r0 error + if rf, ok := ret.Get(0).(func(interface{}) error); ok { + r0 = rf(m) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ResourceService_WatchListClient_RecvMsg_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RecvMsg' +type ResourceService_WatchListClient_RecvMsg_Call struct { + *mock.Call +} + +// RecvMsg is a helper method to define mock.On call +// - m interface{} +func (_e *ResourceService_WatchListClient_Expecter) RecvMsg(m interface{}) *ResourceService_WatchListClient_RecvMsg_Call { + return &ResourceService_WatchListClient_RecvMsg_Call{Call: _e.mock.On("RecvMsg", m)} +} + +func (_c *ResourceService_WatchListClient_RecvMsg_Call) Run(run func(m interface{})) *ResourceService_WatchListClient_RecvMsg_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(interface{})) + }) + return _c +} + +func (_c *ResourceService_WatchListClient_RecvMsg_Call) Return(_a0 error) *ResourceService_WatchListClient_RecvMsg_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ResourceService_WatchListClient_RecvMsg_Call) RunAndReturn(run func(interface{}) error) *ResourceService_WatchListClient_RecvMsg_Call { + _c.Call.Return(run) + return _c +} + +// SendMsg provides a mock function with given fields: m +func (_m *ResourceService_WatchListClient) SendMsg(m interface{}) error { + ret := _m.Called(m) + + var r0 error + if rf, ok := ret.Get(0).(func(interface{}) error); ok { + r0 = rf(m) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ResourceService_WatchListClient_SendMsg_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendMsg' +type ResourceService_WatchListClient_SendMsg_Call struct { + *mock.Call +} + +// SendMsg is a helper method to define mock.On call +// - m interface{} +func (_e *ResourceService_WatchListClient_Expecter) SendMsg(m interface{}) *ResourceService_WatchListClient_SendMsg_Call { + return &ResourceService_WatchListClient_SendMsg_Call{Call: _e.mock.On("SendMsg", m)} +} + +func (_c *ResourceService_WatchListClient_SendMsg_Call) Run(run func(m interface{})) *ResourceService_WatchListClient_SendMsg_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(interface{})) + }) + return _c +} + +func (_c *ResourceService_WatchListClient_SendMsg_Call) Return(_a0 error) *ResourceService_WatchListClient_SendMsg_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ResourceService_WatchListClient_SendMsg_Call) RunAndReturn(run func(interface{}) error) *ResourceService_WatchListClient_SendMsg_Call { + _c.Call.Return(run) + return _c +} + +// Trailer provides a mock function with given fields: +func (_m *ResourceService_WatchListClient) Trailer() metadata.MD { + ret := _m.Called() + + var r0 metadata.MD + if rf, ok := ret.Get(0).(func() metadata.MD); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(metadata.MD) + } + } + + return r0 +} + +// ResourceService_WatchListClient_Trailer_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Trailer' +type ResourceService_WatchListClient_Trailer_Call struct { + *mock.Call +} + +// Trailer is a helper method to define mock.On call +func (_e *ResourceService_WatchListClient_Expecter) Trailer() *ResourceService_WatchListClient_Trailer_Call { + return &ResourceService_WatchListClient_Trailer_Call{Call: _e.mock.On("Trailer")} +} + +func (_c *ResourceService_WatchListClient_Trailer_Call) Run(run func()) *ResourceService_WatchListClient_Trailer_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ResourceService_WatchListClient_Trailer_Call) Return(_a0 metadata.MD) *ResourceService_WatchListClient_Trailer_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ResourceService_WatchListClient_Trailer_Call) RunAndReturn(run func() metadata.MD) *ResourceService_WatchListClient_Trailer_Call { + _c.Call.Return(run) + return _c +} + +// NewResourceService_WatchListClient creates a new instance of ResourceService_WatchListClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewResourceService_WatchListClient(t interface { + mock.TestingT + Cleanup(func()) +}) *ResourceService_WatchListClient { + mock := &ResourceService_WatchListClient{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/grpcmocks/proto-public/pbresource/mock_ResourceService_WatchListServer.go b/grpcmocks/proto-public/pbresource/mock_ResourceService_WatchListServer.go new file mode 100644 index 0000000000..1b9dee399f --- /dev/null +++ b/grpcmocks/proto-public/pbresource/mock_ResourceService_WatchListServer.go @@ -0,0 +1,325 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package mockpbresource + +import ( + context "context" + + mock "github.com/stretchr/testify/mock" + metadata "google.golang.org/grpc/metadata" + + pbresource "github.com/hashicorp/consul/proto-public/pbresource" +) + +// ResourceService_WatchListServer is an autogenerated mock type for the ResourceService_WatchListServer type +type ResourceService_WatchListServer struct { + mock.Mock +} + +type ResourceService_WatchListServer_Expecter struct { + mock *mock.Mock +} + +func (_m *ResourceService_WatchListServer) EXPECT() *ResourceService_WatchListServer_Expecter { + return &ResourceService_WatchListServer_Expecter{mock: &_m.Mock} +} + +// Context provides a mock function with given fields: +func (_m *ResourceService_WatchListServer) Context() context.Context { + ret := _m.Called() + + var r0 context.Context + if rf, ok := ret.Get(0).(func() context.Context); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(context.Context) + } + } + + return r0 +} + +// ResourceService_WatchListServer_Context_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Context' +type ResourceService_WatchListServer_Context_Call struct { + *mock.Call +} + +// Context is a helper method to define mock.On call +func (_e *ResourceService_WatchListServer_Expecter) Context() *ResourceService_WatchListServer_Context_Call { + return &ResourceService_WatchListServer_Context_Call{Call: _e.mock.On("Context")} +} + +func (_c *ResourceService_WatchListServer_Context_Call) Run(run func()) *ResourceService_WatchListServer_Context_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ResourceService_WatchListServer_Context_Call) Return(_a0 context.Context) *ResourceService_WatchListServer_Context_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ResourceService_WatchListServer_Context_Call) RunAndReturn(run func() context.Context) *ResourceService_WatchListServer_Context_Call { + _c.Call.Return(run) + return _c +} + +// RecvMsg provides a mock function with given fields: m +func (_m *ResourceService_WatchListServer) RecvMsg(m interface{}) error { + ret := _m.Called(m) + + var r0 error + if rf, ok := ret.Get(0).(func(interface{}) error); ok { + r0 = rf(m) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ResourceService_WatchListServer_RecvMsg_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RecvMsg' +type ResourceService_WatchListServer_RecvMsg_Call struct { + *mock.Call +} + +// RecvMsg is a helper method to define mock.On call +// - m interface{} +func (_e *ResourceService_WatchListServer_Expecter) RecvMsg(m interface{}) *ResourceService_WatchListServer_RecvMsg_Call { + return &ResourceService_WatchListServer_RecvMsg_Call{Call: _e.mock.On("RecvMsg", m)} +} + +func (_c *ResourceService_WatchListServer_RecvMsg_Call) Run(run func(m interface{})) *ResourceService_WatchListServer_RecvMsg_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(interface{})) + }) + return _c +} + +func (_c *ResourceService_WatchListServer_RecvMsg_Call) Return(_a0 error) *ResourceService_WatchListServer_RecvMsg_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ResourceService_WatchListServer_RecvMsg_Call) RunAndReturn(run func(interface{}) error) *ResourceService_WatchListServer_RecvMsg_Call { + _c.Call.Return(run) + return _c +} + +// Send provides a mock function with given fields: _a0 +func (_m *ResourceService_WatchListServer) Send(_a0 *pbresource.WatchEvent) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(*pbresource.WatchEvent) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ResourceService_WatchListServer_Send_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Send' +type ResourceService_WatchListServer_Send_Call struct { + *mock.Call +} + +// Send is a helper method to define mock.On call +// - _a0 *pbresource.WatchEvent +func (_e *ResourceService_WatchListServer_Expecter) Send(_a0 interface{}) *ResourceService_WatchListServer_Send_Call { + return &ResourceService_WatchListServer_Send_Call{Call: _e.mock.On("Send", _a0)} +} + +func (_c *ResourceService_WatchListServer_Send_Call) Run(run func(_a0 *pbresource.WatchEvent)) *ResourceService_WatchListServer_Send_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*pbresource.WatchEvent)) + }) + return _c +} + +func (_c *ResourceService_WatchListServer_Send_Call) Return(_a0 error) *ResourceService_WatchListServer_Send_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ResourceService_WatchListServer_Send_Call) RunAndReturn(run func(*pbresource.WatchEvent) error) *ResourceService_WatchListServer_Send_Call { + _c.Call.Return(run) + return _c +} + +// SendHeader provides a mock function with given fields: _a0 +func (_m *ResourceService_WatchListServer) SendHeader(_a0 metadata.MD) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(metadata.MD) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ResourceService_WatchListServer_SendHeader_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendHeader' +type ResourceService_WatchListServer_SendHeader_Call struct { + *mock.Call +} + +// SendHeader is a helper method to define mock.On call +// - _a0 metadata.MD +func (_e *ResourceService_WatchListServer_Expecter) SendHeader(_a0 interface{}) *ResourceService_WatchListServer_SendHeader_Call { + return &ResourceService_WatchListServer_SendHeader_Call{Call: _e.mock.On("SendHeader", _a0)} +} + +func (_c *ResourceService_WatchListServer_SendHeader_Call) Run(run func(_a0 metadata.MD)) *ResourceService_WatchListServer_SendHeader_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(metadata.MD)) + }) + return _c +} + +func (_c *ResourceService_WatchListServer_SendHeader_Call) Return(_a0 error) *ResourceService_WatchListServer_SendHeader_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ResourceService_WatchListServer_SendHeader_Call) RunAndReturn(run func(metadata.MD) error) *ResourceService_WatchListServer_SendHeader_Call { + _c.Call.Return(run) + return _c +} + +// SendMsg provides a mock function with given fields: m +func (_m *ResourceService_WatchListServer) SendMsg(m interface{}) error { + ret := _m.Called(m) + + var r0 error + if rf, ok := ret.Get(0).(func(interface{}) error); ok { + r0 = rf(m) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ResourceService_WatchListServer_SendMsg_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendMsg' +type ResourceService_WatchListServer_SendMsg_Call struct { + *mock.Call +} + +// SendMsg is a helper method to define mock.On call +// - m interface{} +func (_e *ResourceService_WatchListServer_Expecter) SendMsg(m interface{}) *ResourceService_WatchListServer_SendMsg_Call { + return &ResourceService_WatchListServer_SendMsg_Call{Call: _e.mock.On("SendMsg", m)} +} + +func (_c *ResourceService_WatchListServer_SendMsg_Call) Run(run func(m interface{})) *ResourceService_WatchListServer_SendMsg_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(interface{})) + }) + return _c +} + +func (_c *ResourceService_WatchListServer_SendMsg_Call) Return(_a0 error) *ResourceService_WatchListServer_SendMsg_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ResourceService_WatchListServer_SendMsg_Call) RunAndReturn(run func(interface{}) error) *ResourceService_WatchListServer_SendMsg_Call { + _c.Call.Return(run) + return _c +} + +// SetHeader provides a mock function with given fields: _a0 +func (_m *ResourceService_WatchListServer) SetHeader(_a0 metadata.MD) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(metadata.MD) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ResourceService_WatchListServer_SetHeader_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetHeader' +type ResourceService_WatchListServer_SetHeader_Call struct { + *mock.Call +} + +// SetHeader is a helper method to define mock.On call +// - _a0 metadata.MD +func (_e *ResourceService_WatchListServer_Expecter) SetHeader(_a0 interface{}) *ResourceService_WatchListServer_SetHeader_Call { + return &ResourceService_WatchListServer_SetHeader_Call{Call: _e.mock.On("SetHeader", _a0)} +} + +func (_c *ResourceService_WatchListServer_SetHeader_Call) Run(run func(_a0 metadata.MD)) *ResourceService_WatchListServer_SetHeader_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(metadata.MD)) + }) + return _c +} + +func (_c *ResourceService_WatchListServer_SetHeader_Call) Return(_a0 error) *ResourceService_WatchListServer_SetHeader_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ResourceService_WatchListServer_SetHeader_Call) RunAndReturn(run func(metadata.MD) error) *ResourceService_WatchListServer_SetHeader_Call { + _c.Call.Return(run) + return _c +} + +// SetTrailer provides a mock function with given fields: _a0 +func (_m *ResourceService_WatchListServer) SetTrailer(_a0 metadata.MD) { + _m.Called(_a0) +} + +// ResourceService_WatchListServer_SetTrailer_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetTrailer' +type ResourceService_WatchListServer_SetTrailer_Call struct { + *mock.Call +} + +// SetTrailer is a helper method to define mock.On call +// - _a0 metadata.MD +func (_e *ResourceService_WatchListServer_Expecter) SetTrailer(_a0 interface{}) *ResourceService_WatchListServer_SetTrailer_Call { + return &ResourceService_WatchListServer_SetTrailer_Call{Call: _e.mock.On("SetTrailer", _a0)} +} + +func (_c *ResourceService_WatchListServer_SetTrailer_Call) Run(run func(_a0 metadata.MD)) *ResourceService_WatchListServer_SetTrailer_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(metadata.MD)) + }) + return _c +} + +func (_c *ResourceService_WatchListServer_SetTrailer_Call) Return() *ResourceService_WatchListServer_SetTrailer_Call { + _c.Call.Return() + return _c +} + +func (_c *ResourceService_WatchListServer_SetTrailer_Call) RunAndReturn(run func(metadata.MD)) *ResourceService_WatchListServer_SetTrailer_Call { + _c.Call.Return(run) + return _c +} + +// NewResourceService_WatchListServer creates a new instance of ResourceService_WatchListServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewResourceService_WatchListServer(t interface { + mock.TestingT + Cleanup(func()) +}) *ResourceService_WatchListServer { + mock := &ResourceService_WatchListServer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/grpcmocks/proto-public/pbresource/mock_UnsafeResourceServiceServer.go b/grpcmocks/proto-public/pbresource/mock_UnsafeResourceServiceServer.go new file mode 100644 index 0000000000..4327f93134 --- /dev/null +++ b/grpcmocks/proto-public/pbresource/mock_UnsafeResourceServiceServer.go @@ -0,0 +1,64 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package mockpbresource + +import mock "github.com/stretchr/testify/mock" + +// UnsafeResourceServiceServer is an autogenerated mock type for the UnsafeResourceServiceServer type +type UnsafeResourceServiceServer struct { + mock.Mock +} + +type UnsafeResourceServiceServer_Expecter struct { + mock *mock.Mock +} + +func (_m *UnsafeResourceServiceServer) EXPECT() *UnsafeResourceServiceServer_Expecter { + return &UnsafeResourceServiceServer_Expecter{mock: &_m.Mock} +} + +// mustEmbedUnimplementedResourceServiceServer provides a mock function with given fields: +func (_m *UnsafeResourceServiceServer) mustEmbedUnimplementedResourceServiceServer() { + _m.Called() +} + +// UnsafeResourceServiceServer_mustEmbedUnimplementedResourceServiceServer_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'mustEmbedUnimplementedResourceServiceServer' +type UnsafeResourceServiceServer_mustEmbedUnimplementedResourceServiceServer_Call struct { + *mock.Call +} + +// mustEmbedUnimplementedResourceServiceServer is a helper method to define mock.On call +func (_e *UnsafeResourceServiceServer_Expecter) mustEmbedUnimplementedResourceServiceServer() *UnsafeResourceServiceServer_mustEmbedUnimplementedResourceServiceServer_Call { + return &UnsafeResourceServiceServer_mustEmbedUnimplementedResourceServiceServer_Call{Call: _e.mock.On("mustEmbedUnimplementedResourceServiceServer")} +} + +func (_c *UnsafeResourceServiceServer_mustEmbedUnimplementedResourceServiceServer_Call) Run(run func()) *UnsafeResourceServiceServer_mustEmbedUnimplementedResourceServiceServer_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UnsafeResourceServiceServer_mustEmbedUnimplementedResourceServiceServer_Call) Return() *UnsafeResourceServiceServer_mustEmbedUnimplementedResourceServiceServer_Call { + _c.Call.Return() + return _c +} + +func (_c *UnsafeResourceServiceServer_mustEmbedUnimplementedResourceServiceServer_Call) RunAndReturn(run func()) *UnsafeResourceServiceServer_mustEmbedUnimplementedResourceServiceServer_Call { + _c.Call.Return(run) + return _c +} + +// NewUnsafeResourceServiceServer creates a new instance of UnsafeResourceServiceServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewUnsafeResourceServiceServer(t interface { + mock.TestingT + Cleanup(func()) +}) *UnsafeResourceServiceServer { + mock := &UnsafeResourceServiceServer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/grpcmocks/proto-public/pbserverdiscovery/mock_ServerDiscoveryServiceClient.go b/grpcmocks/proto-public/pbserverdiscovery/mock_ServerDiscoveryServiceClient.go new file mode 100644 index 0000000000..f6c5ce3b85 --- /dev/null +++ b/grpcmocks/proto-public/pbserverdiscovery/mock_ServerDiscoveryServiceClient.go @@ -0,0 +1,110 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package mockpbserverdiscovery + +import ( + context "context" + + grpc "google.golang.org/grpc" + + mock "github.com/stretchr/testify/mock" + + pbserverdiscovery "github.com/hashicorp/consul/proto-public/pbserverdiscovery" +) + +// ServerDiscoveryServiceClient is an autogenerated mock type for the ServerDiscoveryServiceClient type +type ServerDiscoveryServiceClient struct { + mock.Mock +} + +type ServerDiscoveryServiceClient_Expecter struct { + mock *mock.Mock +} + +func (_m *ServerDiscoveryServiceClient) EXPECT() *ServerDiscoveryServiceClient_Expecter { + return &ServerDiscoveryServiceClient_Expecter{mock: &_m.Mock} +} + +// WatchServers provides a mock function with given fields: ctx, in, opts +func (_m *ServerDiscoveryServiceClient) WatchServers(ctx context.Context, in *pbserverdiscovery.WatchServersRequest, opts ...grpc.CallOption) (pbserverdiscovery.ServerDiscoveryService_WatchServersClient, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 pbserverdiscovery.ServerDiscoveryService_WatchServersClient + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *pbserverdiscovery.WatchServersRequest, ...grpc.CallOption) (pbserverdiscovery.ServerDiscoveryService_WatchServersClient, error)); ok { + return rf(ctx, in, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, *pbserverdiscovery.WatchServersRequest, ...grpc.CallOption) pbserverdiscovery.ServerDiscoveryService_WatchServersClient); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(pbserverdiscovery.ServerDiscoveryService_WatchServersClient) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *pbserverdiscovery.WatchServersRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ServerDiscoveryServiceClient_WatchServers_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WatchServers' +type ServerDiscoveryServiceClient_WatchServers_Call struct { + *mock.Call +} + +// WatchServers is a helper method to define mock.On call +// - ctx context.Context +// - in *pbserverdiscovery.WatchServersRequest +// - opts ...grpc.CallOption +func (_e *ServerDiscoveryServiceClient_Expecter) WatchServers(ctx interface{}, in interface{}, opts ...interface{}) *ServerDiscoveryServiceClient_WatchServers_Call { + return &ServerDiscoveryServiceClient_WatchServers_Call{Call: _e.mock.On("WatchServers", + append([]interface{}{ctx, in}, opts...)...)} +} + +func (_c *ServerDiscoveryServiceClient_WatchServers_Call) Run(run func(ctx context.Context, in *pbserverdiscovery.WatchServersRequest, opts ...grpc.CallOption)) *ServerDiscoveryServiceClient_WatchServers_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]grpc.CallOption, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(grpc.CallOption) + } + } + run(args[0].(context.Context), args[1].(*pbserverdiscovery.WatchServersRequest), variadicArgs...) + }) + return _c +} + +func (_c *ServerDiscoveryServiceClient_WatchServers_Call) Return(_a0 pbserverdiscovery.ServerDiscoveryService_WatchServersClient, _a1 error) *ServerDiscoveryServiceClient_WatchServers_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ServerDiscoveryServiceClient_WatchServers_Call) RunAndReturn(run func(context.Context, *pbserverdiscovery.WatchServersRequest, ...grpc.CallOption) (pbserverdiscovery.ServerDiscoveryService_WatchServersClient, error)) *ServerDiscoveryServiceClient_WatchServers_Call { + _c.Call.Return(run) + return _c +} + +// NewServerDiscoveryServiceClient creates a new instance of ServerDiscoveryServiceClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewServerDiscoveryServiceClient(t interface { + mock.TestingT + Cleanup(func()) +}) *ServerDiscoveryServiceClient { + mock := &ServerDiscoveryServiceClient{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/grpcmocks/proto-public/pbserverdiscovery/mock_ServerDiscoveryServiceServer.go b/grpcmocks/proto-public/pbserverdiscovery/mock_ServerDiscoveryServiceServer.go new file mode 100644 index 0000000000..44b610f9a9 --- /dev/null +++ b/grpcmocks/proto-public/pbserverdiscovery/mock_ServerDiscoveryServiceServer.go @@ -0,0 +1,78 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package mockpbserverdiscovery + +import ( + pbserverdiscovery "github.com/hashicorp/consul/proto-public/pbserverdiscovery" + mock "github.com/stretchr/testify/mock" +) + +// ServerDiscoveryServiceServer is an autogenerated mock type for the ServerDiscoveryServiceServer type +type ServerDiscoveryServiceServer struct { + mock.Mock +} + +type ServerDiscoveryServiceServer_Expecter struct { + mock *mock.Mock +} + +func (_m *ServerDiscoveryServiceServer) EXPECT() *ServerDiscoveryServiceServer_Expecter { + return &ServerDiscoveryServiceServer_Expecter{mock: &_m.Mock} +} + +// WatchServers provides a mock function with given fields: _a0, _a1 +func (_m *ServerDiscoveryServiceServer) WatchServers(_a0 *pbserverdiscovery.WatchServersRequest, _a1 pbserverdiscovery.ServerDiscoveryService_WatchServersServer) error { + ret := _m.Called(_a0, _a1) + + var r0 error + if rf, ok := ret.Get(0).(func(*pbserverdiscovery.WatchServersRequest, pbserverdiscovery.ServerDiscoveryService_WatchServersServer) error); ok { + r0 = rf(_a0, _a1) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ServerDiscoveryServiceServer_WatchServers_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WatchServers' +type ServerDiscoveryServiceServer_WatchServers_Call struct { + *mock.Call +} + +// WatchServers is a helper method to define mock.On call +// - _a0 *pbserverdiscovery.WatchServersRequest +// - _a1 pbserverdiscovery.ServerDiscoveryService_WatchServersServer +func (_e *ServerDiscoveryServiceServer_Expecter) WatchServers(_a0 interface{}, _a1 interface{}) *ServerDiscoveryServiceServer_WatchServers_Call { + return &ServerDiscoveryServiceServer_WatchServers_Call{Call: _e.mock.On("WatchServers", _a0, _a1)} +} + +func (_c *ServerDiscoveryServiceServer_WatchServers_Call) Run(run func(_a0 *pbserverdiscovery.WatchServersRequest, _a1 pbserverdiscovery.ServerDiscoveryService_WatchServersServer)) *ServerDiscoveryServiceServer_WatchServers_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*pbserverdiscovery.WatchServersRequest), args[1].(pbserverdiscovery.ServerDiscoveryService_WatchServersServer)) + }) + return _c +} + +func (_c *ServerDiscoveryServiceServer_WatchServers_Call) Return(_a0 error) *ServerDiscoveryServiceServer_WatchServers_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ServerDiscoveryServiceServer_WatchServers_Call) RunAndReturn(run func(*pbserverdiscovery.WatchServersRequest, pbserverdiscovery.ServerDiscoveryService_WatchServersServer) error) *ServerDiscoveryServiceServer_WatchServers_Call { + _c.Call.Return(run) + return _c +} + +// NewServerDiscoveryServiceServer creates a new instance of ServerDiscoveryServiceServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewServerDiscoveryServiceServer(t interface { + mock.TestingT + Cleanup(func()) +}) *ServerDiscoveryServiceServer { + mock := &ServerDiscoveryServiceServer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/grpcmocks/proto-public/pbserverdiscovery/mock_ServerDiscoveryService_WatchServersClient.go b/grpcmocks/proto-public/pbserverdiscovery/mock_ServerDiscoveryService_WatchServersClient.go new file mode 100644 index 0000000000..9289c84b49 --- /dev/null +++ b/grpcmocks/proto-public/pbserverdiscovery/mock_ServerDiscoveryService_WatchServersClient.go @@ -0,0 +1,356 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package mockpbserverdiscovery + +import ( + context "context" + + mock "github.com/stretchr/testify/mock" + metadata "google.golang.org/grpc/metadata" + + pbserverdiscovery "github.com/hashicorp/consul/proto-public/pbserverdiscovery" +) + +// ServerDiscoveryService_WatchServersClient is an autogenerated mock type for the ServerDiscoveryService_WatchServersClient type +type ServerDiscoveryService_WatchServersClient struct { + mock.Mock +} + +type ServerDiscoveryService_WatchServersClient_Expecter struct { + mock *mock.Mock +} + +func (_m *ServerDiscoveryService_WatchServersClient) EXPECT() *ServerDiscoveryService_WatchServersClient_Expecter { + return &ServerDiscoveryService_WatchServersClient_Expecter{mock: &_m.Mock} +} + +// CloseSend provides a mock function with given fields: +func (_m *ServerDiscoveryService_WatchServersClient) CloseSend() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ServerDiscoveryService_WatchServersClient_CloseSend_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CloseSend' +type ServerDiscoveryService_WatchServersClient_CloseSend_Call struct { + *mock.Call +} + +// CloseSend is a helper method to define mock.On call +func (_e *ServerDiscoveryService_WatchServersClient_Expecter) CloseSend() *ServerDiscoveryService_WatchServersClient_CloseSend_Call { + return &ServerDiscoveryService_WatchServersClient_CloseSend_Call{Call: _e.mock.On("CloseSend")} +} + +func (_c *ServerDiscoveryService_WatchServersClient_CloseSend_Call) Run(run func()) *ServerDiscoveryService_WatchServersClient_CloseSend_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ServerDiscoveryService_WatchServersClient_CloseSend_Call) Return(_a0 error) *ServerDiscoveryService_WatchServersClient_CloseSend_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ServerDiscoveryService_WatchServersClient_CloseSend_Call) RunAndReturn(run func() error) *ServerDiscoveryService_WatchServersClient_CloseSend_Call { + _c.Call.Return(run) + return _c +} + +// Context provides a mock function with given fields: +func (_m *ServerDiscoveryService_WatchServersClient) Context() context.Context { + ret := _m.Called() + + var r0 context.Context + if rf, ok := ret.Get(0).(func() context.Context); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(context.Context) + } + } + + return r0 +} + +// ServerDiscoveryService_WatchServersClient_Context_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Context' +type ServerDiscoveryService_WatchServersClient_Context_Call struct { + *mock.Call +} + +// Context is a helper method to define mock.On call +func (_e *ServerDiscoveryService_WatchServersClient_Expecter) Context() *ServerDiscoveryService_WatchServersClient_Context_Call { + return &ServerDiscoveryService_WatchServersClient_Context_Call{Call: _e.mock.On("Context")} +} + +func (_c *ServerDiscoveryService_WatchServersClient_Context_Call) Run(run func()) *ServerDiscoveryService_WatchServersClient_Context_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ServerDiscoveryService_WatchServersClient_Context_Call) Return(_a0 context.Context) *ServerDiscoveryService_WatchServersClient_Context_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ServerDiscoveryService_WatchServersClient_Context_Call) RunAndReturn(run func() context.Context) *ServerDiscoveryService_WatchServersClient_Context_Call { + _c.Call.Return(run) + return _c +} + +// Header provides a mock function with given fields: +func (_m *ServerDiscoveryService_WatchServersClient) Header() (metadata.MD, error) { + ret := _m.Called() + + var r0 metadata.MD + var r1 error + if rf, ok := ret.Get(0).(func() (metadata.MD, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() metadata.MD); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(metadata.MD) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ServerDiscoveryService_WatchServersClient_Header_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Header' +type ServerDiscoveryService_WatchServersClient_Header_Call struct { + *mock.Call +} + +// Header is a helper method to define mock.On call +func (_e *ServerDiscoveryService_WatchServersClient_Expecter) Header() *ServerDiscoveryService_WatchServersClient_Header_Call { + return &ServerDiscoveryService_WatchServersClient_Header_Call{Call: _e.mock.On("Header")} +} + +func (_c *ServerDiscoveryService_WatchServersClient_Header_Call) Run(run func()) *ServerDiscoveryService_WatchServersClient_Header_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ServerDiscoveryService_WatchServersClient_Header_Call) Return(_a0 metadata.MD, _a1 error) *ServerDiscoveryService_WatchServersClient_Header_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ServerDiscoveryService_WatchServersClient_Header_Call) RunAndReturn(run func() (metadata.MD, error)) *ServerDiscoveryService_WatchServersClient_Header_Call { + _c.Call.Return(run) + return _c +} + +// Recv provides a mock function with given fields: +func (_m *ServerDiscoveryService_WatchServersClient) Recv() (*pbserverdiscovery.WatchServersResponse, error) { + ret := _m.Called() + + var r0 *pbserverdiscovery.WatchServersResponse + var r1 error + if rf, ok := ret.Get(0).(func() (*pbserverdiscovery.WatchServersResponse, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() *pbserverdiscovery.WatchServersResponse); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbserverdiscovery.WatchServersResponse) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ServerDiscoveryService_WatchServersClient_Recv_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Recv' +type ServerDiscoveryService_WatchServersClient_Recv_Call struct { + *mock.Call +} + +// Recv is a helper method to define mock.On call +func (_e *ServerDiscoveryService_WatchServersClient_Expecter) Recv() *ServerDiscoveryService_WatchServersClient_Recv_Call { + return &ServerDiscoveryService_WatchServersClient_Recv_Call{Call: _e.mock.On("Recv")} +} + +func (_c *ServerDiscoveryService_WatchServersClient_Recv_Call) Run(run func()) *ServerDiscoveryService_WatchServersClient_Recv_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ServerDiscoveryService_WatchServersClient_Recv_Call) Return(_a0 *pbserverdiscovery.WatchServersResponse, _a1 error) *ServerDiscoveryService_WatchServersClient_Recv_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ServerDiscoveryService_WatchServersClient_Recv_Call) RunAndReturn(run func() (*pbserverdiscovery.WatchServersResponse, error)) *ServerDiscoveryService_WatchServersClient_Recv_Call { + _c.Call.Return(run) + return _c +} + +// RecvMsg provides a mock function with given fields: m +func (_m *ServerDiscoveryService_WatchServersClient) RecvMsg(m interface{}) error { + ret := _m.Called(m) + + var r0 error + if rf, ok := ret.Get(0).(func(interface{}) error); ok { + r0 = rf(m) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ServerDiscoveryService_WatchServersClient_RecvMsg_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RecvMsg' +type ServerDiscoveryService_WatchServersClient_RecvMsg_Call struct { + *mock.Call +} + +// RecvMsg is a helper method to define mock.On call +// - m interface{} +func (_e *ServerDiscoveryService_WatchServersClient_Expecter) RecvMsg(m interface{}) *ServerDiscoveryService_WatchServersClient_RecvMsg_Call { + return &ServerDiscoveryService_WatchServersClient_RecvMsg_Call{Call: _e.mock.On("RecvMsg", m)} +} + +func (_c *ServerDiscoveryService_WatchServersClient_RecvMsg_Call) Run(run func(m interface{})) *ServerDiscoveryService_WatchServersClient_RecvMsg_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(interface{})) + }) + return _c +} + +func (_c *ServerDiscoveryService_WatchServersClient_RecvMsg_Call) Return(_a0 error) *ServerDiscoveryService_WatchServersClient_RecvMsg_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ServerDiscoveryService_WatchServersClient_RecvMsg_Call) RunAndReturn(run func(interface{}) error) *ServerDiscoveryService_WatchServersClient_RecvMsg_Call { + _c.Call.Return(run) + return _c +} + +// SendMsg provides a mock function with given fields: m +func (_m *ServerDiscoveryService_WatchServersClient) SendMsg(m interface{}) error { + ret := _m.Called(m) + + var r0 error + if rf, ok := ret.Get(0).(func(interface{}) error); ok { + r0 = rf(m) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ServerDiscoveryService_WatchServersClient_SendMsg_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendMsg' +type ServerDiscoveryService_WatchServersClient_SendMsg_Call struct { + *mock.Call +} + +// SendMsg is a helper method to define mock.On call +// - m interface{} +func (_e *ServerDiscoveryService_WatchServersClient_Expecter) SendMsg(m interface{}) *ServerDiscoveryService_WatchServersClient_SendMsg_Call { + return &ServerDiscoveryService_WatchServersClient_SendMsg_Call{Call: _e.mock.On("SendMsg", m)} +} + +func (_c *ServerDiscoveryService_WatchServersClient_SendMsg_Call) Run(run func(m interface{})) *ServerDiscoveryService_WatchServersClient_SendMsg_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(interface{})) + }) + return _c +} + +func (_c *ServerDiscoveryService_WatchServersClient_SendMsg_Call) Return(_a0 error) *ServerDiscoveryService_WatchServersClient_SendMsg_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ServerDiscoveryService_WatchServersClient_SendMsg_Call) RunAndReturn(run func(interface{}) error) *ServerDiscoveryService_WatchServersClient_SendMsg_Call { + _c.Call.Return(run) + return _c +} + +// Trailer provides a mock function with given fields: +func (_m *ServerDiscoveryService_WatchServersClient) Trailer() metadata.MD { + ret := _m.Called() + + var r0 metadata.MD + if rf, ok := ret.Get(0).(func() metadata.MD); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(metadata.MD) + } + } + + return r0 +} + +// ServerDiscoveryService_WatchServersClient_Trailer_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Trailer' +type ServerDiscoveryService_WatchServersClient_Trailer_Call struct { + *mock.Call +} + +// Trailer is a helper method to define mock.On call +func (_e *ServerDiscoveryService_WatchServersClient_Expecter) Trailer() *ServerDiscoveryService_WatchServersClient_Trailer_Call { + return &ServerDiscoveryService_WatchServersClient_Trailer_Call{Call: _e.mock.On("Trailer")} +} + +func (_c *ServerDiscoveryService_WatchServersClient_Trailer_Call) Run(run func()) *ServerDiscoveryService_WatchServersClient_Trailer_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ServerDiscoveryService_WatchServersClient_Trailer_Call) Return(_a0 metadata.MD) *ServerDiscoveryService_WatchServersClient_Trailer_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ServerDiscoveryService_WatchServersClient_Trailer_Call) RunAndReturn(run func() metadata.MD) *ServerDiscoveryService_WatchServersClient_Trailer_Call { + _c.Call.Return(run) + return _c +} + +// NewServerDiscoveryService_WatchServersClient creates a new instance of ServerDiscoveryService_WatchServersClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewServerDiscoveryService_WatchServersClient(t interface { + mock.TestingT + Cleanup(func()) +}) *ServerDiscoveryService_WatchServersClient { + mock := &ServerDiscoveryService_WatchServersClient{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/grpcmocks/proto-public/pbserverdiscovery/mock_ServerDiscoveryService_WatchServersServer.go b/grpcmocks/proto-public/pbserverdiscovery/mock_ServerDiscoveryService_WatchServersServer.go new file mode 100644 index 0000000000..b80039ec5b --- /dev/null +++ b/grpcmocks/proto-public/pbserverdiscovery/mock_ServerDiscoveryService_WatchServersServer.go @@ -0,0 +1,325 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package mockpbserverdiscovery + +import ( + context "context" + + mock "github.com/stretchr/testify/mock" + metadata "google.golang.org/grpc/metadata" + + pbserverdiscovery "github.com/hashicorp/consul/proto-public/pbserverdiscovery" +) + +// ServerDiscoveryService_WatchServersServer is an autogenerated mock type for the ServerDiscoveryService_WatchServersServer type +type ServerDiscoveryService_WatchServersServer struct { + mock.Mock +} + +type ServerDiscoveryService_WatchServersServer_Expecter struct { + mock *mock.Mock +} + +func (_m *ServerDiscoveryService_WatchServersServer) EXPECT() *ServerDiscoveryService_WatchServersServer_Expecter { + return &ServerDiscoveryService_WatchServersServer_Expecter{mock: &_m.Mock} +} + +// Context provides a mock function with given fields: +func (_m *ServerDiscoveryService_WatchServersServer) Context() context.Context { + ret := _m.Called() + + var r0 context.Context + if rf, ok := ret.Get(0).(func() context.Context); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(context.Context) + } + } + + return r0 +} + +// ServerDiscoveryService_WatchServersServer_Context_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Context' +type ServerDiscoveryService_WatchServersServer_Context_Call struct { + *mock.Call +} + +// Context is a helper method to define mock.On call +func (_e *ServerDiscoveryService_WatchServersServer_Expecter) Context() *ServerDiscoveryService_WatchServersServer_Context_Call { + return &ServerDiscoveryService_WatchServersServer_Context_Call{Call: _e.mock.On("Context")} +} + +func (_c *ServerDiscoveryService_WatchServersServer_Context_Call) Run(run func()) *ServerDiscoveryService_WatchServersServer_Context_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ServerDiscoveryService_WatchServersServer_Context_Call) Return(_a0 context.Context) *ServerDiscoveryService_WatchServersServer_Context_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ServerDiscoveryService_WatchServersServer_Context_Call) RunAndReturn(run func() context.Context) *ServerDiscoveryService_WatchServersServer_Context_Call { + _c.Call.Return(run) + return _c +} + +// RecvMsg provides a mock function with given fields: m +func (_m *ServerDiscoveryService_WatchServersServer) RecvMsg(m interface{}) error { + ret := _m.Called(m) + + var r0 error + if rf, ok := ret.Get(0).(func(interface{}) error); ok { + r0 = rf(m) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ServerDiscoveryService_WatchServersServer_RecvMsg_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RecvMsg' +type ServerDiscoveryService_WatchServersServer_RecvMsg_Call struct { + *mock.Call +} + +// RecvMsg is a helper method to define mock.On call +// - m interface{} +func (_e *ServerDiscoveryService_WatchServersServer_Expecter) RecvMsg(m interface{}) *ServerDiscoveryService_WatchServersServer_RecvMsg_Call { + return &ServerDiscoveryService_WatchServersServer_RecvMsg_Call{Call: _e.mock.On("RecvMsg", m)} +} + +func (_c *ServerDiscoveryService_WatchServersServer_RecvMsg_Call) Run(run func(m interface{})) *ServerDiscoveryService_WatchServersServer_RecvMsg_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(interface{})) + }) + return _c +} + +func (_c *ServerDiscoveryService_WatchServersServer_RecvMsg_Call) Return(_a0 error) *ServerDiscoveryService_WatchServersServer_RecvMsg_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ServerDiscoveryService_WatchServersServer_RecvMsg_Call) RunAndReturn(run func(interface{}) error) *ServerDiscoveryService_WatchServersServer_RecvMsg_Call { + _c.Call.Return(run) + return _c +} + +// Send provides a mock function with given fields: _a0 +func (_m *ServerDiscoveryService_WatchServersServer) Send(_a0 *pbserverdiscovery.WatchServersResponse) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(*pbserverdiscovery.WatchServersResponse) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ServerDiscoveryService_WatchServersServer_Send_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Send' +type ServerDiscoveryService_WatchServersServer_Send_Call struct { + *mock.Call +} + +// Send is a helper method to define mock.On call +// - _a0 *pbserverdiscovery.WatchServersResponse +func (_e *ServerDiscoveryService_WatchServersServer_Expecter) Send(_a0 interface{}) *ServerDiscoveryService_WatchServersServer_Send_Call { + return &ServerDiscoveryService_WatchServersServer_Send_Call{Call: _e.mock.On("Send", _a0)} +} + +func (_c *ServerDiscoveryService_WatchServersServer_Send_Call) Run(run func(_a0 *pbserverdiscovery.WatchServersResponse)) *ServerDiscoveryService_WatchServersServer_Send_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*pbserverdiscovery.WatchServersResponse)) + }) + return _c +} + +func (_c *ServerDiscoveryService_WatchServersServer_Send_Call) Return(_a0 error) *ServerDiscoveryService_WatchServersServer_Send_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ServerDiscoveryService_WatchServersServer_Send_Call) RunAndReturn(run func(*pbserverdiscovery.WatchServersResponse) error) *ServerDiscoveryService_WatchServersServer_Send_Call { + _c.Call.Return(run) + return _c +} + +// SendHeader provides a mock function with given fields: _a0 +func (_m *ServerDiscoveryService_WatchServersServer) SendHeader(_a0 metadata.MD) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(metadata.MD) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ServerDiscoveryService_WatchServersServer_SendHeader_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendHeader' +type ServerDiscoveryService_WatchServersServer_SendHeader_Call struct { + *mock.Call +} + +// SendHeader is a helper method to define mock.On call +// - _a0 metadata.MD +func (_e *ServerDiscoveryService_WatchServersServer_Expecter) SendHeader(_a0 interface{}) *ServerDiscoveryService_WatchServersServer_SendHeader_Call { + return &ServerDiscoveryService_WatchServersServer_SendHeader_Call{Call: _e.mock.On("SendHeader", _a0)} +} + +func (_c *ServerDiscoveryService_WatchServersServer_SendHeader_Call) Run(run func(_a0 metadata.MD)) *ServerDiscoveryService_WatchServersServer_SendHeader_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(metadata.MD)) + }) + return _c +} + +func (_c *ServerDiscoveryService_WatchServersServer_SendHeader_Call) Return(_a0 error) *ServerDiscoveryService_WatchServersServer_SendHeader_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ServerDiscoveryService_WatchServersServer_SendHeader_Call) RunAndReturn(run func(metadata.MD) error) *ServerDiscoveryService_WatchServersServer_SendHeader_Call { + _c.Call.Return(run) + return _c +} + +// SendMsg provides a mock function with given fields: m +func (_m *ServerDiscoveryService_WatchServersServer) SendMsg(m interface{}) error { + ret := _m.Called(m) + + var r0 error + if rf, ok := ret.Get(0).(func(interface{}) error); ok { + r0 = rf(m) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ServerDiscoveryService_WatchServersServer_SendMsg_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendMsg' +type ServerDiscoveryService_WatchServersServer_SendMsg_Call struct { + *mock.Call +} + +// SendMsg is a helper method to define mock.On call +// - m interface{} +func (_e *ServerDiscoveryService_WatchServersServer_Expecter) SendMsg(m interface{}) *ServerDiscoveryService_WatchServersServer_SendMsg_Call { + return &ServerDiscoveryService_WatchServersServer_SendMsg_Call{Call: _e.mock.On("SendMsg", m)} +} + +func (_c *ServerDiscoveryService_WatchServersServer_SendMsg_Call) Run(run func(m interface{})) *ServerDiscoveryService_WatchServersServer_SendMsg_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(interface{})) + }) + return _c +} + +func (_c *ServerDiscoveryService_WatchServersServer_SendMsg_Call) Return(_a0 error) *ServerDiscoveryService_WatchServersServer_SendMsg_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ServerDiscoveryService_WatchServersServer_SendMsg_Call) RunAndReturn(run func(interface{}) error) *ServerDiscoveryService_WatchServersServer_SendMsg_Call { + _c.Call.Return(run) + return _c +} + +// SetHeader provides a mock function with given fields: _a0 +func (_m *ServerDiscoveryService_WatchServersServer) SetHeader(_a0 metadata.MD) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(metadata.MD) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ServerDiscoveryService_WatchServersServer_SetHeader_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetHeader' +type ServerDiscoveryService_WatchServersServer_SetHeader_Call struct { + *mock.Call +} + +// SetHeader is a helper method to define mock.On call +// - _a0 metadata.MD +func (_e *ServerDiscoveryService_WatchServersServer_Expecter) SetHeader(_a0 interface{}) *ServerDiscoveryService_WatchServersServer_SetHeader_Call { + return &ServerDiscoveryService_WatchServersServer_SetHeader_Call{Call: _e.mock.On("SetHeader", _a0)} +} + +func (_c *ServerDiscoveryService_WatchServersServer_SetHeader_Call) Run(run func(_a0 metadata.MD)) *ServerDiscoveryService_WatchServersServer_SetHeader_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(metadata.MD)) + }) + return _c +} + +func (_c *ServerDiscoveryService_WatchServersServer_SetHeader_Call) Return(_a0 error) *ServerDiscoveryService_WatchServersServer_SetHeader_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ServerDiscoveryService_WatchServersServer_SetHeader_Call) RunAndReturn(run func(metadata.MD) error) *ServerDiscoveryService_WatchServersServer_SetHeader_Call { + _c.Call.Return(run) + return _c +} + +// SetTrailer provides a mock function with given fields: _a0 +func (_m *ServerDiscoveryService_WatchServersServer) SetTrailer(_a0 metadata.MD) { + _m.Called(_a0) +} + +// ServerDiscoveryService_WatchServersServer_SetTrailer_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetTrailer' +type ServerDiscoveryService_WatchServersServer_SetTrailer_Call struct { + *mock.Call +} + +// SetTrailer is a helper method to define mock.On call +// - _a0 metadata.MD +func (_e *ServerDiscoveryService_WatchServersServer_Expecter) SetTrailer(_a0 interface{}) *ServerDiscoveryService_WatchServersServer_SetTrailer_Call { + return &ServerDiscoveryService_WatchServersServer_SetTrailer_Call{Call: _e.mock.On("SetTrailer", _a0)} +} + +func (_c *ServerDiscoveryService_WatchServersServer_SetTrailer_Call) Run(run func(_a0 metadata.MD)) *ServerDiscoveryService_WatchServersServer_SetTrailer_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(metadata.MD)) + }) + return _c +} + +func (_c *ServerDiscoveryService_WatchServersServer_SetTrailer_Call) Return() *ServerDiscoveryService_WatchServersServer_SetTrailer_Call { + _c.Call.Return() + return _c +} + +func (_c *ServerDiscoveryService_WatchServersServer_SetTrailer_Call) RunAndReturn(run func(metadata.MD)) *ServerDiscoveryService_WatchServersServer_SetTrailer_Call { + _c.Call.Return(run) + return _c +} + +// NewServerDiscoveryService_WatchServersServer creates a new instance of ServerDiscoveryService_WatchServersServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewServerDiscoveryService_WatchServersServer(t interface { + mock.TestingT + Cleanup(func()) +}) *ServerDiscoveryService_WatchServersServer { + mock := &ServerDiscoveryService_WatchServersServer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/grpcmocks/proto-public/pbserverdiscovery/mock_UnsafeServerDiscoveryServiceServer.go b/grpcmocks/proto-public/pbserverdiscovery/mock_UnsafeServerDiscoveryServiceServer.go new file mode 100644 index 0000000000..f749949ace --- /dev/null +++ b/grpcmocks/proto-public/pbserverdiscovery/mock_UnsafeServerDiscoveryServiceServer.go @@ -0,0 +1,64 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package mockpbserverdiscovery + +import mock "github.com/stretchr/testify/mock" + +// UnsafeServerDiscoveryServiceServer is an autogenerated mock type for the UnsafeServerDiscoveryServiceServer type +type UnsafeServerDiscoveryServiceServer struct { + mock.Mock +} + +type UnsafeServerDiscoveryServiceServer_Expecter struct { + mock *mock.Mock +} + +func (_m *UnsafeServerDiscoveryServiceServer) EXPECT() *UnsafeServerDiscoveryServiceServer_Expecter { + return &UnsafeServerDiscoveryServiceServer_Expecter{mock: &_m.Mock} +} + +// mustEmbedUnimplementedServerDiscoveryServiceServer provides a mock function with given fields: +func (_m *UnsafeServerDiscoveryServiceServer) mustEmbedUnimplementedServerDiscoveryServiceServer() { + _m.Called() +} + +// UnsafeServerDiscoveryServiceServer_mustEmbedUnimplementedServerDiscoveryServiceServer_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'mustEmbedUnimplementedServerDiscoveryServiceServer' +type UnsafeServerDiscoveryServiceServer_mustEmbedUnimplementedServerDiscoveryServiceServer_Call struct { + *mock.Call +} + +// mustEmbedUnimplementedServerDiscoveryServiceServer is a helper method to define mock.On call +func (_e *UnsafeServerDiscoveryServiceServer_Expecter) mustEmbedUnimplementedServerDiscoveryServiceServer() *UnsafeServerDiscoveryServiceServer_mustEmbedUnimplementedServerDiscoveryServiceServer_Call { + return &UnsafeServerDiscoveryServiceServer_mustEmbedUnimplementedServerDiscoveryServiceServer_Call{Call: _e.mock.On("mustEmbedUnimplementedServerDiscoveryServiceServer")} +} + +func (_c *UnsafeServerDiscoveryServiceServer_mustEmbedUnimplementedServerDiscoveryServiceServer_Call) Run(run func()) *UnsafeServerDiscoveryServiceServer_mustEmbedUnimplementedServerDiscoveryServiceServer_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *UnsafeServerDiscoveryServiceServer_mustEmbedUnimplementedServerDiscoveryServiceServer_Call) Return() *UnsafeServerDiscoveryServiceServer_mustEmbedUnimplementedServerDiscoveryServiceServer_Call { + _c.Call.Return() + return _c +} + +func (_c *UnsafeServerDiscoveryServiceServer_mustEmbedUnimplementedServerDiscoveryServiceServer_Call) RunAndReturn(run func()) *UnsafeServerDiscoveryServiceServer_mustEmbedUnimplementedServerDiscoveryServiceServer_Call { + _c.Call.Return(run) + return _c +} + +// NewUnsafeServerDiscoveryServiceServer creates a new instance of UnsafeServerDiscoveryServiceServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewUnsafeServerDiscoveryServiceServer(t interface { + mock.TestingT + Cleanup(func()) +}) *UnsafeServerDiscoveryServiceServer { + mock := &UnsafeServerDiscoveryServiceServer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/auth/internal/controllers/trafficpermissions/controller.go b/internal/auth/internal/controllers/trafficpermissions/controller.go index 9e5912dc5e..d7e9b127ec 100644 --- a/internal/auth/internal/controllers/trafficpermissions/controller.go +++ b/internal/auth/internal/controllers/trafficpermissions/controller.go @@ -10,6 +10,7 @@ import ( "google.golang.org/protobuf/types/known/anypb" "github.com/hashicorp/consul/internal/controller" + "github.com/hashicorp/consul/internal/controller/dependency" "github.com/hashicorp/consul/internal/resource" pbauth "github.com/hashicorp/consul/proto-public/pbauth/v2beta1" "github.com/hashicorp/consul/proto-public/pbresource" @@ -32,13 +33,13 @@ type TrafficPermissionsMapper interface { // Controller creates a controller for automatic ComputedTrafficPermissions management for // updates to WorkloadIdentity or TrafficPermission resources. -func Controller(mapper TrafficPermissionsMapper) controller.Controller { +func Controller(mapper TrafficPermissionsMapper) *controller.Controller { if mapper == nil { panic("No TrafficPermissionsMapper was provided to the TrafficPermissionsController constructor") } - return controller.ForType(pbauth.ComputedTrafficPermissionsType). - WithWatch(pbauth.WorkloadIdentityType, controller.ReplaceType(pbauth.ComputedTrafficPermissionsType)). + return controller.NewController(StatusKey, pbauth.ComputedTrafficPermissionsType). + WithWatch(pbauth.WorkloadIdentityType, dependency.ReplaceType(pbauth.ComputedTrafficPermissionsType)). WithWatch(pbauth.TrafficPermissionsType, mapper.MapTrafficPermissions). WithReconciler(&reconciler{mapper: mapper}) } diff --git a/internal/catalog/internal/controllers/endpoints/controller.go b/internal/catalog/internal/controllers/endpoints/controller.go index ef0c20f979..d34ad7c893 100644 --- a/internal/catalog/internal/controllers/endpoints/controller.go +++ b/internal/catalog/internal/controllers/endpoints/controller.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/consul/internal/catalog/internal/controllers/workloadhealth" "github.com/hashicorp/consul/internal/controller" + "github.com/hashicorp/consul/internal/controller/dependency" "github.com/hashicorp/consul/internal/resource" pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v2beta1" "github.com/hashicorp/consul/proto-public/pbresource" @@ -38,13 +39,13 @@ type WorkloadMapper interface { // ServiceEndpointsController creates a controller to perform automatic endpoint management for // services. -func ServiceEndpointsController(workloadMap WorkloadMapper) controller.Controller { +func ServiceEndpointsController(workloadMap WorkloadMapper) *controller.Controller { if workloadMap == nil { panic("No WorkloadMapper was provided to the ServiceEndpointsController constructor") } - return controller.ForType(pbcatalog.ServiceEndpointsType). - WithWatch(pbcatalog.ServiceType, controller.ReplaceType(pbcatalog.ServiceEndpointsType)). + return controller.NewController(StatusKey, pbcatalog.ServiceEndpointsType). + WithWatch(pbcatalog.ServiceType, dependency.ReplaceType(pbcatalog.ServiceEndpointsType)). WithWatch(pbcatalog.WorkloadType, workloadMap.MapWorkload). WithReconciler(newServiceEndpointsReconciler(workloadMap)) } diff --git a/internal/catalog/internal/controllers/failover/controller.go b/internal/catalog/internal/controllers/failover/controller.go index ef236b954c..4dcf4f432c 100644 --- a/internal/catalog/internal/controllers/failover/controller.go +++ b/internal/catalog/internal/controllers/failover/controller.go @@ -31,11 +31,11 @@ type FailoverMapper interface { MapService(ctx context.Context, rt controller.Runtime, res *pbresource.Resource) ([]controller.Request, error) } -func FailoverPolicyController(mapper FailoverMapper) controller.Controller { +func FailoverPolicyController(mapper FailoverMapper) *controller.Controller { if mapper == nil { panic("No FailoverMapper was provided to the FailoverPolicyController constructor") } - return controller.ForType(pbcatalog.FailoverPolicyType). + return controller.NewController(StatusKey, pbcatalog.FailoverPolicyType). WithWatch(pbcatalog.ServiceType, mapper.MapService). WithReconciler(newFailoverPolicyReconciler(mapper)) } diff --git a/internal/catalog/internal/controllers/nodehealth/controller.go b/internal/catalog/internal/controllers/nodehealth/controller.go index f28bb7a134..8502872169 100644 --- a/internal/catalog/internal/controllers/nodehealth/controller.go +++ b/internal/catalog/internal/controllers/nodehealth/controller.go @@ -11,14 +11,15 @@ import ( "google.golang.org/grpc/status" "github.com/hashicorp/consul/internal/controller" + "github.com/hashicorp/consul/internal/controller/dependency" "github.com/hashicorp/consul/internal/resource" pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v2beta1" "github.com/hashicorp/consul/proto-public/pbresource" ) -func NodeHealthController() controller.Controller { - return controller.ForType(pbcatalog.NodeType). - WithWatch(pbcatalog.NodeHealthStatusType, controller.MapOwnerFiltered(pbcatalog.NodeType)). +func NodeHealthController() *controller.Controller { + return controller.NewController(StatusKey, pbcatalog.NodeType). + WithWatch(pbcatalog.NodeHealthStatusType, dependency.MapOwnerFiltered(pbcatalog.NodeType)). WithReconciler(&nodeHealthReconciler{}) } diff --git a/internal/catalog/internal/controllers/workloadhealth/controller.go b/internal/catalog/internal/controllers/workloadhealth/controller.go index 05cfd36a84..c21d2e070f 100644 --- a/internal/catalog/internal/controllers/workloadhealth/controller.go +++ b/internal/catalog/internal/controllers/workloadhealth/controller.go @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/consul/internal/catalog/internal/controllers/nodehealth" "github.com/hashicorp/consul/internal/controller" + "github.com/hashicorp/consul/internal/controller/dependency" "github.com/hashicorp/consul/internal/resource" pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v2beta1" "github.com/hashicorp/consul/proto-public/pbresource" @@ -45,13 +46,13 @@ type NodeMapper interface { NodeIDFromWorkload(workload *pbresource.Resource, workloadData *pbcatalog.Workload) *pbresource.ID } -func WorkloadHealthController(nodeMap NodeMapper) controller.Controller { +func WorkloadHealthController(nodeMap NodeMapper) *controller.Controller { if nodeMap == nil { panic("No NodeMapper was provided to the WorkloadHealthController constructor") } - return controller.ForType(pbcatalog.WorkloadType). - WithWatch(pbcatalog.HealthStatusType, controller.MapOwnerFiltered(pbcatalog.WorkloadType)). + return controller.NewController(StatusKey, pbcatalog.WorkloadType). + WithWatch(pbcatalog.HealthStatusType, dependency.MapOwnerFiltered(pbcatalog.WorkloadType)). WithWatch(pbcatalog.NodeType, nodeMap.MapNodeToWorkloads). WithReconciler(&workloadHealthReconciler{nodeMap: nodeMap}) } diff --git a/internal/controller/.mockery.yaml b/internal/controller/.mockery.yaml new file mode 100644 index 0000000000..dddcc58a35 --- /dev/null +++ b/internal/controller/.mockery.yaml @@ -0,0 +1,11 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +with-expecter: true +all: true +recursive: false +dir: "controllermock" +outpkg: "controllermock" +mockname: "{{.InterfaceName}}" +packages: + github.com/hashicorp/consul/internal/controller: diff --git a/internal/controller/api.go b/internal/controller/api.go deleted file mode 100644 index 5c2fc2e782..0000000000 --- a/internal/controller/api.go +++ /dev/null @@ -1,260 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package controller - -import ( - "context" - "fmt" - "strings" - "time" - - "github.com/hashicorp/go-hclog" - - "github.com/hashicorp/consul/agent/consul/controller/queue" - "github.com/hashicorp/consul/internal/resource" - "github.com/hashicorp/consul/proto-public/pbresource" -) - -// ForType begins building a Controller for the given resource type. -func ForType(managedType *pbresource.Type) Controller { - return Controller{managedType: managedType} -} - -// WithReconciler changes the controller's reconciler. -func (c Controller) WithReconciler(reconciler Reconciler) Controller { - if reconciler == nil { - panic("reconciler must not be nil") - } - - c.reconciler = reconciler - return c -} - -// WithWatch adds a watch on the given type/dependency to the controller. mapper -// will be called to determine which resources must be reconciled as a result of -// a watched resource changing. -func (c Controller) WithWatch(watchedType *pbresource.Type, mapper DependencyMapper) Controller { - if watchedType == nil { - panic("watchedType must not be nil") - } - - if mapper == nil { - panic("mapper must not be nil") - } - - c.watches = append(c.watches, watch{watchedType, mapper}) - return c -} - -// WithCustomWatch adds a custom watch on the given dependency to the controller. Custom mapper -// will be called to map events produced by source to the controller's watched type. -func (c Controller) WithCustomWatch(source *Source, mapper CustomDependencyMapper) Controller { - if source == nil { - panic("source must not be nil") - } - - if mapper == nil { - panic("mapper must not be nil") - } - - c.customWatches = append(c.customWatches, customWatch{source, mapper}) - return c -} - -// WithLogger changes the controller's logger. -func (c Controller) WithLogger(logger hclog.Logger) Controller { - if logger == nil { - panic("logger must not be nil") - } - - c.logger = logger - return c -} - -// WithBackoff changes the base and maximum backoff values for the controller's -// retry rate limiter. -func (c Controller) WithBackoff(base, max time.Duration) Controller { - c.baseBackoff = base - c.maxBackoff = max - return c -} - -// WithPlacement changes where and how many replicas of the controller will run. -// In the majority of cases, the default placement (one leader elected instance -// per cluster) is the most appropriate and you shouldn't need to override it. -func (c Controller) WithPlacement(placement Placement) Controller { - c.placement = placement - return c -} - -// String returns a textual description of the controller, useful for debugging. -func (c Controller) String() string { - watchedTypes := make([]string, len(c.watches)) - for idx, w := range c.watches { - watchedTypes[idx] = fmt.Sprintf("%q", resource.ToGVK(w.watchedType)) - } - base, max := c.backoff() - return fmt.Sprintf( - ", placement=%q>", - resource.ToGVK(c.managedType), - strings.Join(watchedTypes, ", "), - base, max, - c.placement, - ) -} - -func (c Controller) backoff() (time.Duration, time.Duration) { - base := c.baseBackoff - if base == 0 { - base = 5 * time.Millisecond - } - max := c.maxBackoff - if max == 0 { - max = 1000 * time.Second - } - return base, max -} - -// Controller runs a reconciliation loop to respond to changes in resources and -// their dependencies. It is heavily inspired by Kubernetes' controller pattern: -// https://kubernetes.io/docs/concepts/architecture/controller/ -// -// Use the builder methods in this package (starting with ForType) to construct -// a controller, and then pass it to a Manager to be executed. -type Controller struct { - managedType *pbresource.Type - reconciler Reconciler - logger hclog.Logger - watches []watch - customWatches []customWatch - baseBackoff time.Duration - maxBackoff time.Duration - placement Placement -} - -type watch struct { - watchedType *pbresource.Type - mapper DependencyMapper -} - -// Watch is responsible for watching for custom events from source and adding them to -// the event queue. -func (s *Source) Watch(ctx context.Context, add func(e Event)) error { - for { - select { - case <-ctx.Done(): - return nil - case evt, ok := <-s.Source: - if !ok { - return nil - } - add(evt) - } - } -} - -// Source is used as a generic source of events. This can be used when events aren't coming from resources -// stored by the resource API. -type Source struct { - Source <-chan Event -} - -// Event captures an event in the system which the API can choose to respond to. -type Event struct { - Obj queue.ItemType -} - -// Key returns a string that will be used to de-duplicate items in the queue. -func (e Event) Key() string { - return e.Obj.Key() -} - -// customWatch represent a Watch on a custom Event source and a Mapper to map said -// Events into Requests that the controller can respond to. -type customWatch struct { - source *Source - mapper CustomDependencyMapper -} - -// Request represents a request to reconcile the resource with the given ID. -type Request struct { - // ID of the resource that needs to be reconciled. - ID *pbresource.ID -} - -// Key satisfies the queue.ItemType interface. It returns a string which will be -// used to de-duplicate requests in the queue. -func (r Request) Key() string { - return fmt.Sprintf( - "part=%q,peer=%q,ns=%q,name=%q,uid=%q", - r.ID.Tenancy.Partition, - r.ID.Tenancy.PeerName, - r.ID.Tenancy.Namespace, - r.ID.Name, - r.ID.Uid, - ) -} - -// Runtime contains the dependencies required by reconcilers. -type Runtime struct { - Client pbresource.ResourceServiceClient - Logger hclog.Logger -} - -// Reconciler implements the business logic of a controller. -type Reconciler interface { - // Reconcile the resource identified by req.ID. - Reconcile(ctx context.Context, rt Runtime, req Request) error -} - -// Placement determines where and how many replicas of the controller will run. -type Placement int - -const ( - // PlacementSingleton ensures there is a single, leader-elected, instance of - // the controller running in the cluster at any time. It's the default and is - // suitable for most use-cases. - PlacementSingleton Placement = iota - - // PlacementEachServer ensures there is a replica of the controller running on - // each server in the cluster. It is useful for cases where the controller is - // responsible for applying some configuration resource to the server whenever - // it changes (e.g. rate-limit configuration). Generally, controllers in this - // placement mode should not modify resources. - PlacementEachServer -) - -// String satisfies the fmt.Stringer interface. -func (p Placement) String() string { - switch p { - case PlacementSingleton: - return "singleton" - case PlacementEachServer: - return "each-server" - } - panic(fmt.Sprintf("unknown placement %d", p)) -} - -// RequeueAfterError is an error that allows a Reconciler to override the -// exponential backoff behavior of the Controller, rather than applying -// the backoff algorithm, returning a RequeueAfterError will cause the -// Controller to reschedule the Request at a given time in the future. -type RequeueAfterError time.Duration - -// Error implements the error interface. -func (r RequeueAfterError) Error() string { - return fmt.Sprintf("requeue at %s", time.Duration(r)) -} - -// RequeueAfter constructs a RequeueAfterError with the given duration -// setting. -func RequeueAfter(after time.Duration) error { - return RequeueAfterError(after) -} - -// RequeueNow constructs a RequeueAfterError that reschedules the Request -// immediately. -func RequeueNow() error { - return RequeueAfterError(0) -} diff --git a/internal/controller/cache/.mockery.yaml b/internal/controller/cache/.mockery.yaml new file mode 100644 index 0000000000..e5bd108165 --- /dev/null +++ b/internal/controller/cache/.mockery.yaml @@ -0,0 +1,15 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +with-expecter: true +recursive: false +all: true +# We don't want the mocks within proto-public so as to force a dependency +# of the testify library on the modules usage. The mocks are only for +# internal testing purposes. Other consumers can generated the mocks into +# their own code base. +dir: "{{.PackageName}}mock" +outpkg: "{{.PackageName}}mock" +mockname: "{{.InterfaceName}}" +packages: + github.com/hashicorp/consul/internal/controller/cache: diff --git a/internal/controller/cache/cache.go b/internal/controller/cache/cache.go new file mode 100644 index 0000000000..2f501838f6 --- /dev/null +++ b/internal/controller/cache/cache.go @@ -0,0 +1,213 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package cache + +import ( + "github.com/hashicorp/consul/internal/controller/cache/index" + "github.com/hashicorp/consul/proto-public/pbresource" +) + +// Query is the function type to use for named query callbacks +type Query func(c ReadOnlyCache, args ...any) (ResourceIterator, error) + +type Cache interface { + // AddType will add a new resource type to the cache. This will + // include configuring an `id` index based on the resources Id + AddType(it *pbresource.Type) + // AddIndex will add a new index for the specified type to the cache. + // If the type isn't yet known to the cache, it will first add it + // including setting up its `id` index. + AddIndex(it *pbresource.Type, index *index.Index) error + // AddQuery will add a new named query to the cache. This query + // can potentially use multiple different cache indexes to come + // up with the final result iterator. + AddQuery(name string, fn Query) error + + ReadOnlyCache + WriteCache +} + +// ReadOnlyCache is the set of methods on the Resource cache that can be used +// to query the cache. +type ReadOnlyCache interface { + // Get retrieves a single resource from the specified index that matches the provided args. + // If more than one match is found the first is returned. + Get(it *pbresource.Type, indexName string, args ...any) (*pbresource.Resource, error) + + // List retrieves all the resources from the specified index matching the provided args. + List(it *pbresource.Type, indexName string, args ...any) ([]*pbresource.Resource, error) + + // ListIterator retrieves an iterator over all resources from the specified index matching the provided args. + ListIterator(it *pbresource.Type, indexName string, args ...any) (ResourceIterator, error) + + // Parents retrieves all resources whos index value is a parent (or prefix) of the value calculated + // from the provided args. + Parents(it *pbresource.Type, indexName string, args ...any) ([]*pbresource.Resource, error) + + // ParentsIterator retrieves an iterator over all resources whos index value is a parent (or prefix) + // of the value calculated from the provided args. + ParentsIterator(it *pbresource.Type, indexName string, args ...any) (ResourceIterator, error) + + // Query will execute a named query against the cache and return an interator over its results + Query(name string, args ...any) (ResourceIterator, error) +} + +type WriteCache interface { + // Insert will add a single resource into the cache. If it already exists, this will update + // all indexing to the current values. + Insert(r *pbresource.Resource) error + + // Delete will remove a single resource from the cache. + Delete(r *pbresource.Resource) error +} + +type ResourceIterator interface { + Next() *pbresource.Resource +} + +type unversionedType struct { + Group string + Kind string +} + +type cache struct { + kinds map[unversionedType]*kindIndices + queries map[string]Query +} + +func New() Cache { + return newCache() +} + +func newCache() *cache { + return &cache{ + kinds: make(map[unversionedType]*kindIndices), + queries: make(map[string]Query), + } +} + +func (c *cache) ensureTypeCached(it *pbresource.Type) *kindIndices { + ut := unversionedType{Group: it.Group, Kind: it.Kind} + + _, ok := c.kinds[ut] + if !ok { + c.kinds[ut] = newKindIndices() + } + + return c.kinds[ut] +} + +func (c *cache) AddType(it *pbresource.Type) { + c.ensureTypeCached(it) +} + +func (c *cache) AddIndex(it *pbresource.Type, index *index.Index) error { + kind := c.ensureTypeCached(it) + err := kind.addIndex(index) + if err != nil { + return CacheTypeError{it: unversionedType{Group: it.Group, Kind: it.Kind}, err: err} + } + return nil +} + +func (c *cache) AddQuery(name string, fn Query) error { + if fn == nil { + return QueryRequired + } + if _, found := c.queries[name]; found { + return DuplicateQueryError{name: name} + } + + c.queries[name] = fn + return nil +} + +func (c *cache) Query(name string, args ...any) (ResourceIterator, error) { + fn, found := c.queries[name] + if !found { + return nil, QueryNotFoundError{name: name} + } + + return fn(c, args...) +} + +func (c *cache) Get(it *pbresource.Type, indexName string, args ...any) (*pbresource.Resource, error) { + indices, err := c.getTypeIndices(it) + if err != nil { + return nil, err + } + + return indices.get(indexName, args...) +} + +func (c *cache) ListIterator(it *pbresource.Type, indexName string, args ...any) (ResourceIterator, error) { + indices, err := c.getTypeIndices(it) + if err != nil { + return nil, err + } + + return indices.listIterator(indexName, args...) +} + +func (c *cache) List(it *pbresource.Type, indexName string, args ...any) ([]*pbresource.Resource, error) { + return expandIterator(c.ListIterator(it, indexName, args...)) +} + +func (c *cache) ParentsIterator(it *pbresource.Type, indexName string, args ...any) (ResourceIterator, error) { + indices, err := c.getTypeIndices(it) + if err != nil { + return nil, err + } + + return indices.parentsIterator(indexName, args...) +} + +func (c *cache) Parents(it *pbresource.Type, indexName string, args ...any) ([]*pbresource.Resource, error) { + return expandIterator(c.ParentsIterator(it, indexName, args...)) +} + +func (c *cache) Insert(r *pbresource.Resource) error { + indices, err := c.getTypeIndices(r.Id.Type) + if err != nil { + return err + } + + return indices.insert(r) +} + +func (c *cache) Delete(r *pbresource.Resource) error { + indices, err := c.getTypeIndices(r.Id.Type) + if err != nil { + return err + } + + return indices.delete(r) +} + +func (c *cache) getTypeIndices(it *pbresource.Type) (*kindIndices, error) { + if it == nil { + return nil, TypeUnspecifiedError + } + + ut := unversionedType{Group: it.Group, Kind: it.Kind} + + indices, ok := c.kinds[ut] + if !ok { + return nil, TypeNotIndexedError + } + return indices, nil +} + +func expandIterator(iter ResourceIterator, err error) ([]*pbresource.Resource, error) { + if err != nil { + return nil, err + } + + var results []*pbresource.Resource + for res := iter.Next(); res != nil; res = iter.Next() { + results = append(results, res) + } + + return results, nil +} diff --git a/internal/controller/cache/cache_test.go b/internal/controller/cache/cache_test.go new file mode 100644 index 0000000000..705a2d1df9 --- /dev/null +++ b/internal/controller/cache/cache_test.go @@ -0,0 +1,353 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package cache + +import ( + "encoding/binary" + "testing" + + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + "github.com/hashicorp/consul/internal/controller/cache/index" + "github.com/hashicorp/consul/internal/controller/cache/indexers" + "github.com/hashicorp/consul/internal/resource" + "github.com/hashicorp/consul/internal/resource/resourcetest" + "github.com/hashicorp/consul/proto-public/pbresource" + pbdemo "github.com/hashicorp/consul/proto/private/pbdemo/v1" + "github.com/hashicorp/consul/proto/private/prototest" +) + +const ( + errQueryName = "error-query" + okQueryName = "ok-query" +) + +func namePrefixIndexer() *index.Index { + return indexers.DecodedSingleIndexer( + "name_prefix", + index.SingleValueFromArgs(func(value string) ([]byte, error) { + return []byte(value), nil + }), + func(r *resource.DecodedResource[*pbdemo.Album]) (bool, []byte, error) { + return true, []byte(r.Data.Name), nil + }) +} + +func releaseYearIndexer() *index.Index { + return indexers.DecodedSingleIndexer( + "year", + index.SingleValueFromArgs(func(value int32) ([]byte, error) { + var b index.Builder + binary.Write(&b, binary.BigEndian, value) + return b.Bytes(), nil + }), + func(r *resource.DecodedResource[*pbdemo.Album]) (bool, []byte, error) { + var b index.Builder + binary.Write(&b, binary.BigEndian, r.Data.YearOfRelease) + return true, b.Bytes(), nil + }) +} + +func tracksIndexer() *index.Index { + return indexers.DecodedMultiIndexer( + "tracks", + index.SingleValueFromOneOrTwoArgs(func(value string, prefix bool) ([]byte, error) { + var b index.Builder + if prefix { + b.Raw([]byte(value)) + } else { + b.String(value) + } + return b.Bytes(), nil + }), + func(r *resource.DecodedResource[*pbdemo.Album]) (bool, [][]byte, error) { + indexes := make([][]byte, len(r.Data.Tracks)) + for idx, track := range r.Data.Tracks { + var b index.Builder + b.String(track) + indexes[idx] = b.Bytes() + } + + return true, indexes, nil + }) +} + +func requireCacheIndex(t *testing.T, c *cache, rtype *pbresource.Type, indexes ...string) { + t.Helper() + indices, err := c.getTypeIndices(rtype) + require.NoError(t, err) + require.NotNil(t, indices) + + for _, name := range indexes { + index, err := indices.getIndex(name) + require.NoError(t, err) + require.NotNil(t, index) + } +} + +func TestCacheAddType(t *testing.T) { + c := newCache() + c.AddType(pbdemo.AlbumType) + + // Adding a type will ensure that the `id` index exists + requireCacheIndex(t, c, pbdemo.AlbumType, "id") +} + +func TestCacheAddIndex(t *testing.T) { + c := newCache() + require.NoError(t, c.AddIndex(pbdemo.AlbumType, releaseYearIndexer())) + require.NoError(t, c.AddIndex(pbdemo.AlbumType, tracksIndexer())) + + // Adding indexes should also have the side effect of ensuring that the `id` index exists + requireCacheIndex(t, c, pbdemo.AlbumType, "id", "year", "tracks") +} + +func TestCacheAddIndex_Duplicate(t *testing.T) { + c := newCache() + require.NoError(t, c.AddIndex(pbdemo.AlbumType, releaseYearIndexer())) + // should get an error due to a duplicate index name + require.Error(t, c.AddIndex(pbdemo.AlbumType, releaseYearIndexer())) +} + +func noopQuery(_ ReadOnlyCache, _ ...any) (ResourceIterator, error) { + return nil, nil +} + +func errQuery(_ ReadOnlyCache, _ ...any) (ResourceIterator, error) { + return nil, injectedError +} + +func TestCacheAddQuery(t *testing.T) { + c := newCache() + require.NoError(t, c.AddQuery("foo", noopQuery)) + require.NoError(t, c.AddQuery("bar", errQuery)) + + fn, found := c.queries["foo"] + require.True(t, found) + iter, err := fn(c) + require.NoError(t, err) + require.Nil(t, iter) + + fn, found = c.queries["bar"] + require.True(t, found) + iter, err = fn(c) + require.ErrorIs(t, err, injectedError) + require.Nil(t, iter) +} + +func TestCacheAddQuery_Duplicate(t *testing.T) { + c := newCache() + + require.NoError(t, c.AddQuery("foo", noopQuery)) + // should get an error due to a duplicate query name + require.Error(t, c.AddQuery("foo", noopQuery)) +} + +func TestCacheAddQuery_Nil(t *testing.T) { + c := newCache() + require.ErrorIs(t, c.AddQuery("foo", nil), QueryRequired) +} + +func TestQuery_NotFound(t *testing.T) { + c := newCache() + iter, err := c.Query("foo", "something") + require.ErrorIs(t, err, QueryNotFoundError{"foo"}) + require.Nil(t, iter) +} + +func TestCache(t *testing.T) { + suite.Run(t, &cacheSuite{}) +} + +type cacheSuite struct { + suite.Suite + c Cache + + album1 *pbresource.Resource + album2 *pbresource.Resource + album3 *pbresource.Resource + album4 *pbresource.Resource +} + +func (suite *cacheSuite) SetupTest() { + suite.c = New() + + require.NoError(suite.T(), suite.c.AddIndex(pbdemo.AlbumType, namePrefixIndexer())) + require.NoError(suite.T(), suite.c.AddQuery(okQueryName, func(c ReadOnlyCache, args ...any) (ResourceIterator, error) { + return c.ParentsIterator(pbdemo.AlbumType, "name_prefix", args...) + })) + require.NoError(suite.T(), suite.c.AddIndex(pbdemo.AlbumType, releaseYearIndexer())) + require.NoError(suite.T(), suite.c.AddQuery(errQueryName, errQuery)) + require.NoError(suite.T(), suite.c.AddIndex(pbdemo.AlbumType, tracksIndexer())) + + suite.album1 = resourcetest.Resource(pbdemo.AlbumType, "one"). + WithTenancy(resource.DefaultNamespacedTenancy()). + WithData(suite.T(), &pbdemo.Album{ + Name: "one", + YearOfRelease: 2023, + Tracks: []string{"foo", "bar", "baz"}, + }). + Build() + + suite.album2 = resourcetest.Resource(pbdemo.AlbumType, "two"). + WithTenancy(resource.DefaultNamespacedTenancy()). + WithData(suite.T(), &pbdemo.Album{ + Name: "two", + YearOfRelease: 2023, + Tracks: []string{"fangorn", "zoo"}, + }). + Build() + + suite.album3 = resourcetest.Resource(pbdemo.AlbumType, "third"). + WithTenancy(resource.DefaultNamespacedTenancy()). + WithData(suite.T(), &pbdemo.Album{ + Name: "foo", + YearOfRelease: 2022, + Tracks: []string{"blah", "something", "else"}, + }). + Build() + + suite.album4 = resourcetest.Resource(pbdemo.AlbumType, "four"). + WithTenancy(resource.DefaultNamespacedTenancy()). + WithData(suite.T(), &pbdemo.Album{ + Name: "food", + YearOfRelease: 2020, + Tracks: []string{"nothing", "food"}, + }). + Build() + + require.NoError(suite.T(), suite.c.Insert(suite.album1)) + require.NoError(suite.T(), suite.c.Insert(suite.album2)) + require.NoError(suite.T(), suite.c.Insert(suite.album3)) + require.NoError(suite.T(), suite.c.Insert(suite.album4)) +} + +func (suite *cacheSuite) TestGet() { + res, err := suite.c.Get(pbdemo.AlbumType, "id", suite.album1.Id) + require.NoError(suite.T(), err) + prototest.AssertDeepEqual(suite.T(), suite.album1, res) + + res, err = suite.c.Get(pbdemo.AlbumType, "year", int32(2022)) + require.NoError(suite.T(), err) + prototest.AssertDeepEqual(suite.T(), suite.album3, res) + + res, err = suite.c.Get(pbdemo.AlbumType, "tracks", "fangorn") + require.NoError(suite.T(), err) + prototest.AssertDeepEqual(suite.T(), suite.album2, res) +} + +func (suite *cacheSuite) TestGet_NilType() { + res, err := suite.c.Get(nil, "id", suite.album1.Id) + require.Error(suite.T(), err) + require.ErrorIs(suite.T(), err, TypeUnspecifiedError) + require.Nil(suite.T(), res) +} + +func (suite *cacheSuite) TestGet_UncachedType() { + res, err := suite.c.Get(pbdemo.ArtistType, "id", suite.album1.Id) + require.Error(suite.T(), err) + require.ErrorIs(suite.T(), err, TypeNotIndexedError) + require.Nil(suite.T(), res) +} + +func (suite *cacheSuite) TestGet_IndexNotFound() { + res, err := suite.c.Get(pbdemo.AlbumType, "blah", suite.album1.Id) + require.Error(suite.T(), err) + require.ErrorIs(suite.T(), err, IndexNotFoundError{name: "blah"}) + require.Nil(suite.T(), res) +} + +func (suite *cacheSuite) TestList() { + resources, err := suite.c.List(pbdemo.AlbumType, "year", int32(2023)) + require.NoError(suite.T(), err) + prototest.AssertElementsMatch(suite.T(), []*pbresource.Resource{suite.album1, suite.album2}, resources) + + resources, err = suite.c.List(pbdemo.AlbumType, "tracks", "f", true) + require.NoError(suite.T(), err) + prototest.AssertElementsMatch(suite.T(), []*pbresource.Resource{suite.album1, suite.album2, suite.album4}, resources) +} + +func (suite *cacheSuite) TestList_NilType() { + res, err := suite.c.List(nil, "id", suite.album1.Id) + require.Error(suite.T(), err) + require.ErrorIs(suite.T(), err, TypeUnspecifiedError) + require.Nil(suite.T(), res) +} + +func (suite *cacheSuite) TestList_UncachedType() { + res, err := suite.c.List(pbdemo.ArtistType, "id", suite.album1.Id) + require.Error(suite.T(), err) + require.ErrorIs(suite.T(), err, TypeNotIndexedError) + require.Nil(suite.T(), res) +} + +func (suite *cacheSuite) TestList_IndexNotFound() { + res, err := suite.c.List(pbdemo.AlbumType, "blah", suite.album1.Id) + require.Error(suite.T(), err) + require.ErrorIs(suite.T(), err, IndexNotFoundError{name: "blah"}) + require.Nil(suite.T(), res) +} + +func (suite *cacheSuite) TestParents() { + resources, err := suite.c.Parents(pbdemo.AlbumType, "name_prefix", "food") + require.NoError(suite.T(), err) + prototest.AssertElementsMatch(suite.T(), []*pbresource.Resource{suite.album3, suite.album4}, resources) +} + +func (suite *cacheSuite) TestQuery() { + resources, err := expandIterator(suite.c.Query(okQueryName, "food")) + require.NoError(suite.T(), err) + prototest.AssertElementsMatch(suite.T(), []*pbresource.Resource{suite.album3, suite.album4}, resources) +} + +func (suite *cacheSuite) TestParents_NilType() { + res, err := suite.c.Parents(nil, "id", suite.album1.Id) + require.Error(suite.T(), err) + require.ErrorIs(suite.T(), err, TypeUnspecifiedError) + require.Nil(suite.T(), res) +} + +func (suite *cacheSuite) TestParents_UncachedType() { + res, err := suite.c.Parents(pbdemo.ArtistType, "id", suite.album1.Id) + require.Error(suite.T(), err) + require.ErrorIs(suite.T(), err, TypeNotIndexedError) + require.Nil(suite.T(), res) +} + +func (suite *cacheSuite) TestParents_IndexNotFound() { + res, err := suite.c.Parents(pbdemo.AlbumType, "blah", suite.album1.Id) + require.Error(suite.T(), err) + require.ErrorIs(suite.T(), err, IndexNotFoundError{name: "blah"}) + require.Nil(suite.T(), res) +} + +func (suite *cacheSuite) TestInsert_UncachedType() { + err := suite.c.Insert(resourcetest.Resource(pbdemo.ArtistType, "blah").Build()) + require.Error(suite.T(), err) + require.ErrorIs(suite.T(), err, TypeNotIndexedError) +} + +func (suite *cacheSuite) TestDelete() { + err := suite.c.Delete(suite.album1) + require.NoError(suite.T(), err) + + res, err := suite.c.Get(pbdemo.AlbumType, "id", suite.album1.Id) + require.NoError(suite.T(), err) + require.Nil(suite.T(), res) + + resources, err := suite.c.List(pbdemo.AlbumType, "year", int32(2023)) + require.NoError(suite.T(), err) + prototest.AssertElementsMatch(suite.T(), []*pbresource.Resource{suite.album2}, resources) + + resources, err = suite.c.Parents(pbdemo.AlbumType, "name_prefix", "onesie") + require.NoError(suite.T(), err) + require.Nil(suite.T(), resources) +} + +func (suite *cacheSuite) TestDelete_UncachedType() { + err := suite.c.Delete(resourcetest.Resource(pbdemo.ArtistType, "blah").Build()) + require.Error(suite.T(), err) + require.ErrorIs(suite.T(), err, TypeNotIndexedError) +} diff --git a/internal/controller/cache/cachemock/mock_Cache.go b/internal/controller/cache/cachemock/mock_Cache.go new file mode 100644 index 0000000000..2908a4ffcd --- /dev/null +++ b/internal/controller/cache/cachemock/mock_Cache.go @@ -0,0 +1,637 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package cachemock + +import ( + cache "github.com/hashicorp/consul/internal/controller/cache" + index "github.com/hashicorp/consul/internal/controller/cache/index" + + mock "github.com/stretchr/testify/mock" + + pbresource "github.com/hashicorp/consul/proto-public/pbresource" +) + +// Cache is an autogenerated mock type for the Cache type +type Cache struct { + mock.Mock +} + +type Cache_Expecter struct { + mock *mock.Mock +} + +func (_m *Cache) EXPECT() *Cache_Expecter { + return &Cache_Expecter{mock: &_m.Mock} +} + +// AddIndex provides a mock function with given fields: it, _a1 +func (_m *Cache) AddIndex(it *pbresource.Type, _a1 *index.Index) error { + ret := _m.Called(it, _a1) + + var r0 error + if rf, ok := ret.Get(0).(func(*pbresource.Type, *index.Index) error); ok { + r0 = rf(it, _a1) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Cache_AddIndex_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddIndex' +type Cache_AddIndex_Call struct { + *mock.Call +} + +// AddIndex is a helper method to define mock.On call +// - it *pbresource.Type +// - _a1 *index.Index +func (_e *Cache_Expecter) AddIndex(it interface{}, _a1 interface{}) *Cache_AddIndex_Call { + return &Cache_AddIndex_Call{Call: _e.mock.On("AddIndex", it, _a1)} +} + +func (_c *Cache_AddIndex_Call) Run(run func(it *pbresource.Type, _a1 *index.Index)) *Cache_AddIndex_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*pbresource.Type), args[1].(*index.Index)) + }) + return _c +} + +func (_c *Cache_AddIndex_Call) Return(_a0 error) *Cache_AddIndex_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *Cache_AddIndex_Call) RunAndReturn(run func(*pbresource.Type, *index.Index) error) *Cache_AddIndex_Call { + _c.Call.Return(run) + return _c +} + +// AddQuery provides a mock function with given fields: name, fn +func (_m *Cache) AddQuery(name string, fn cache.Query) error { + ret := _m.Called(name, fn) + + var r0 error + if rf, ok := ret.Get(0).(func(string, cache.Query) error); ok { + r0 = rf(name, fn) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Cache_AddQuery_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddQuery' +type Cache_AddQuery_Call struct { + *mock.Call +} + +// AddQuery is a helper method to define mock.On call +// - name string +// - fn cache.Query +func (_e *Cache_Expecter) AddQuery(name interface{}, fn interface{}) *Cache_AddQuery_Call { + return &Cache_AddQuery_Call{Call: _e.mock.On("AddQuery", name, fn)} +} + +func (_c *Cache_AddQuery_Call) Run(run func(name string, fn cache.Query)) *Cache_AddQuery_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(cache.Query)) + }) + return _c +} + +func (_c *Cache_AddQuery_Call) Return(_a0 error) *Cache_AddQuery_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *Cache_AddQuery_Call) RunAndReturn(run func(string, cache.Query) error) *Cache_AddQuery_Call { + _c.Call.Return(run) + return _c +} + +// AddType provides a mock function with given fields: it +func (_m *Cache) AddType(it *pbresource.Type) { + _m.Called(it) +} + +// Cache_AddType_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddType' +type Cache_AddType_Call struct { + *mock.Call +} + +// AddType is a helper method to define mock.On call +// - it *pbresource.Type +func (_e *Cache_Expecter) AddType(it interface{}) *Cache_AddType_Call { + return &Cache_AddType_Call{Call: _e.mock.On("AddType", it)} +} + +func (_c *Cache_AddType_Call) Run(run func(it *pbresource.Type)) *Cache_AddType_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*pbresource.Type)) + }) + return _c +} + +func (_c *Cache_AddType_Call) Return() *Cache_AddType_Call { + _c.Call.Return() + return _c +} + +func (_c *Cache_AddType_Call) RunAndReturn(run func(*pbresource.Type)) *Cache_AddType_Call { + _c.Call.Return(run) + return _c +} + +// Delete provides a mock function with given fields: r +func (_m *Cache) Delete(r *pbresource.Resource) error { + ret := _m.Called(r) + + var r0 error + if rf, ok := ret.Get(0).(func(*pbresource.Resource) error); ok { + r0 = rf(r) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Cache_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete' +type Cache_Delete_Call struct { + *mock.Call +} + +// Delete is a helper method to define mock.On call +// - r *pbresource.Resource +func (_e *Cache_Expecter) Delete(r interface{}) *Cache_Delete_Call { + return &Cache_Delete_Call{Call: _e.mock.On("Delete", r)} +} + +func (_c *Cache_Delete_Call) Run(run func(r *pbresource.Resource)) *Cache_Delete_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*pbresource.Resource)) + }) + return _c +} + +func (_c *Cache_Delete_Call) Return(_a0 error) *Cache_Delete_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *Cache_Delete_Call) RunAndReturn(run func(*pbresource.Resource) error) *Cache_Delete_Call { + _c.Call.Return(run) + return _c +} + +// Get provides a mock function with given fields: it, indexName, args +func (_m *Cache) Get(it *pbresource.Type, indexName string, args ...interface{}) (*pbresource.Resource, error) { + var _ca []interface{} + _ca = append(_ca, it, indexName) + _ca = append(_ca, args...) + ret := _m.Called(_ca...) + + var r0 *pbresource.Resource + var r1 error + if rf, ok := ret.Get(0).(func(*pbresource.Type, string, ...interface{}) (*pbresource.Resource, error)); ok { + return rf(it, indexName, args...) + } + if rf, ok := ret.Get(0).(func(*pbresource.Type, string, ...interface{}) *pbresource.Resource); ok { + r0 = rf(it, indexName, args...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbresource.Resource) + } + } + + if rf, ok := ret.Get(1).(func(*pbresource.Type, string, ...interface{}) error); ok { + r1 = rf(it, indexName, args...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Cache_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get' +type Cache_Get_Call struct { + *mock.Call +} + +// Get is a helper method to define mock.On call +// - it *pbresource.Type +// - indexName string +// - args ...interface{} +func (_e *Cache_Expecter) Get(it interface{}, indexName interface{}, args ...interface{}) *Cache_Get_Call { + return &Cache_Get_Call{Call: _e.mock.On("Get", + append([]interface{}{it, indexName}, args...)...)} +} + +func (_c *Cache_Get_Call) Run(run func(it *pbresource.Type, indexName string, args ...interface{})) *Cache_Get_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]interface{}, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(interface{}) + } + } + run(args[0].(*pbresource.Type), args[1].(string), variadicArgs...) + }) + return _c +} + +func (_c *Cache_Get_Call) Return(_a0 *pbresource.Resource, _a1 error) *Cache_Get_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Cache_Get_Call) RunAndReturn(run func(*pbresource.Type, string, ...interface{}) (*pbresource.Resource, error)) *Cache_Get_Call { + _c.Call.Return(run) + return _c +} + +// Insert provides a mock function with given fields: r +func (_m *Cache) Insert(r *pbresource.Resource) error { + ret := _m.Called(r) + + var r0 error + if rf, ok := ret.Get(0).(func(*pbresource.Resource) error); ok { + r0 = rf(r) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Cache_Insert_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Insert' +type Cache_Insert_Call struct { + *mock.Call +} + +// Insert is a helper method to define mock.On call +// - r *pbresource.Resource +func (_e *Cache_Expecter) Insert(r interface{}) *Cache_Insert_Call { + return &Cache_Insert_Call{Call: _e.mock.On("Insert", r)} +} + +func (_c *Cache_Insert_Call) Run(run func(r *pbresource.Resource)) *Cache_Insert_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*pbresource.Resource)) + }) + return _c +} + +func (_c *Cache_Insert_Call) Return(_a0 error) *Cache_Insert_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *Cache_Insert_Call) RunAndReturn(run func(*pbresource.Resource) error) *Cache_Insert_Call { + _c.Call.Return(run) + return _c +} + +// List provides a mock function with given fields: it, indexName, args +func (_m *Cache) List(it *pbresource.Type, indexName string, args ...interface{}) ([]*pbresource.Resource, error) { + var _ca []interface{} + _ca = append(_ca, it, indexName) + _ca = append(_ca, args...) + ret := _m.Called(_ca...) + + var r0 []*pbresource.Resource + var r1 error + if rf, ok := ret.Get(0).(func(*pbresource.Type, string, ...interface{}) ([]*pbresource.Resource, error)); ok { + return rf(it, indexName, args...) + } + if rf, ok := ret.Get(0).(func(*pbresource.Type, string, ...interface{}) []*pbresource.Resource); ok { + r0 = rf(it, indexName, args...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*pbresource.Resource) + } + } + + if rf, ok := ret.Get(1).(func(*pbresource.Type, string, ...interface{}) error); ok { + r1 = rf(it, indexName, args...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Cache_List_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'List' +type Cache_List_Call struct { + *mock.Call +} + +// List is a helper method to define mock.On call +// - it *pbresource.Type +// - indexName string +// - args ...interface{} +func (_e *Cache_Expecter) List(it interface{}, indexName interface{}, args ...interface{}) *Cache_List_Call { + return &Cache_List_Call{Call: _e.mock.On("List", + append([]interface{}{it, indexName}, args...)...)} +} + +func (_c *Cache_List_Call) Run(run func(it *pbresource.Type, indexName string, args ...interface{})) *Cache_List_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]interface{}, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(interface{}) + } + } + run(args[0].(*pbresource.Type), args[1].(string), variadicArgs...) + }) + return _c +} + +func (_c *Cache_List_Call) Return(_a0 []*pbresource.Resource, _a1 error) *Cache_List_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Cache_List_Call) RunAndReturn(run func(*pbresource.Type, string, ...interface{}) ([]*pbresource.Resource, error)) *Cache_List_Call { + _c.Call.Return(run) + return _c +} + +// ListIterator provides a mock function with given fields: it, indexName, args +func (_m *Cache) ListIterator(it *pbresource.Type, indexName string, args ...interface{}) (cache.ResourceIterator, error) { + var _ca []interface{} + _ca = append(_ca, it, indexName) + _ca = append(_ca, args...) + ret := _m.Called(_ca...) + + var r0 cache.ResourceIterator + var r1 error + if rf, ok := ret.Get(0).(func(*pbresource.Type, string, ...interface{}) (cache.ResourceIterator, error)); ok { + return rf(it, indexName, args...) + } + if rf, ok := ret.Get(0).(func(*pbresource.Type, string, ...interface{}) cache.ResourceIterator); ok { + r0 = rf(it, indexName, args...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(cache.ResourceIterator) + } + } + + if rf, ok := ret.Get(1).(func(*pbresource.Type, string, ...interface{}) error); ok { + r1 = rf(it, indexName, args...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Cache_ListIterator_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListIterator' +type Cache_ListIterator_Call struct { + *mock.Call +} + +// ListIterator is a helper method to define mock.On call +// - it *pbresource.Type +// - indexName string +// - args ...interface{} +func (_e *Cache_Expecter) ListIterator(it interface{}, indexName interface{}, args ...interface{}) *Cache_ListIterator_Call { + return &Cache_ListIterator_Call{Call: _e.mock.On("ListIterator", + append([]interface{}{it, indexName}, args...)...)} +} + +func (_c *Cache_ListIterator_Call) Run(run func(it *pbresource.Type, indexName string, args ...interface{})) *Cache_ListIterator_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]interface{}, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(interface{}) + } + } + run(args[0].(*pbresource.Type), args[1].(string), variadicArgs...) + }) + return _c +} + +func (_c *Cache_ListIterator_Call) Return(_a0 cache.ResourceIterator, _a1 error) *Cache_ListIterator_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Cache_ListIterator_Call) RunAndReturn(run func(*pbresource.Type, string, ...interface{}) (cache.ResourceIterator, error)) *Cache_ListIterator_Call { + _c.Call.Return(run) + return _c +} + +// Parents provides a mock function with given fields: it, indexName, args +func (_m *Cache) Parents(it *pbresource.Type, indexName string, args ...interface{}) ([]*pbresource.Resource, error) { + var _ca []interface{} + _ca = append(_ca, it, indexName) + _ca = append(_ca, args...) + ret := _m.Called(_ca...) + + var r0 []*pbresource.Resource + var r1 error + if rf, ok := ret.Get(0).(func(*pbresource.Type, string, ...interface{}) ([]*pbresource.Resource, error)); ok { + return rf(it, indexName, args...) + } + if rf, ok := ret.Get(0).(func(*pbresource.Type, string, ...interface{}) []*pbresource.Resource); ok { + r0 = rf(it, indexName, args...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*pbresource.Resource) + } + } + + if rf, ok := ret.Get(1).(func(*pbresource.Type, string, ...interface{}) error); ok { + r1 = rf(it, indexName, args...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Cache_Parents_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Parents' +type Cache_Parents_Call struct { + *mock.Call +} + +// Parents is a helper method to define mock.On call +// - it *pbresource.Type +// - indexName string +// - args ...interface{} +func (_e *Cache_Expecter) Parents(it interface{}, indexName interface{}, args ...interface{}) *Cache_Parents_Call { + return &Cache_Parents_Call{Call: _e.mock.On("Parents", + append([]interface{}{it, indexName}, args...)...)} +} + +func (_c *Cache_Parents_Call) Run(run func(it *pbresource.Type, indexName string, args ...interface{})) *Cache_Parents_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]interface{}, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(interface{}) + } + } + run(args[0].(*pbresource.Type), args[1].(string), variadicArgs...) + }) + return _c +} + +func (_c *Cache_Parents_Call) Return(_a0 []*pbresource.Resource, _a1 error) *Cache_Parents_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Cache_Parents_Call) RunAndReturn(run func(*pbresource.Type, string, ...interface{}) ([]*pbresource.Resource, error)) *Cache_Parents_Call { + _c.Call.Return(run) + return _c +} + +// ParentsIterator provides a mock function with given fields: it, indexName, args +func (_m *Cache) ParentsIterator(it *pbresource.Type, indexName string, args ...interface{}) (cache.ResourceIterator, error) { + var _ca []interface{} + _ca = append(_ca, it, indexName) + _ca = append(_ca, args...) + ret := _m.Called(_ca...) + + var r0 cache.ResourceIterator + var r1 error + if rf, ok := ret.Get(0).(func(*pbresource.Type, string, ...interface{}) (cache.ResourceIterator, error)); ok { + return rf(it, indexName, args...) + } + if rf, ok := ret.Get(0).(func(*pbresource.Type, string, ...interface{}) cache.ResourceIterator); ok { + r0 = rf(it, indexName, args...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(cache.ResourceIterator) + } + } + + if rf, ok := ret.Get(1).(func(*pbresource.Type, string, ...interface{}) error); ok { + r1 = rf(it, indexName, args...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Cache_ParentsIterator_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ParentsIterator' +type Cache_ParentsIterator_Call struct { + *mock.Call +} + +// ParentsIterator is a helper method to define mock.On call +// - it *pbresource.Type +// - indexName string +// - args ...interface{} +func (_e *Cache_Expecter) ParentsIterator(it interface{}, indexName interface{}, args ...interface{}) *Cache_ParentsIterator_Call { + return &Cache_ParentsIterator_Call{Call: _e.mock.On("ParentsIterator", + append([]interface{}{it, indexName}, args...)...)} +} + +func (_c *Cache_ParentsIterator_Call) Run(run func(it *pbresource.Type, indexName string, args ...interface{})) *Cache_ParentsIterator_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]interface{}, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(interface{}) + } + } + run(args[0].(*pbresource.Type), args[1].(string), variadicArgs...) + }) + return _c +} + +func (_c *Cache_ParentsIterator_Call) Return(_a0 cache.ResourceIterator, _a1 error) *Cache_ParentsIterator_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Cache_ParentsIterator_Call) RunAndReturn(run func(*pbresource.Type, string, ...interface{}) (cache.ResourceIterator, error)) *Cache_ParentsIterator_Call { + _c.Call.Return(run) + return _c +} + +// Query provides a mock function with given fields: name, args +func (_m *Cache) Query(name string, args ...interface{}) (cache.ResourceIterator, error) { + var _ca []interface{} + _ca = append(_ca, name) + _ca = append(_ca, args...) + ret := _m.Called(_ca...) + + var r0 cache.ResourceIterator + var r1 error + if rf, ok := ret.Get(0).(func(string, ...interface{}) (cache.ResourceIterator, error)); ok { + return rf(name, args...) + } + if rf, ok := ret.Get(0).(func(string, ...interface{}) cache.ResourceIterator); ok { + r0 = rf(name, args...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(cache.ResourceIterator) + } + } + + if rf, ok := ret.Get(1).(func(string, ...interface{}) error); ok { + r1 = rf(name, args...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Cache_Query_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Query' +type Cache_Query_Call struct { + *mock.Call +} + +// Query is a helper method to define mock.On call +// - name string +// - args ...interface{} +func (_e *Cache_Expecter) Query(name interface{}, args ...interface{}) *Cache_Query_Call { + return &Cache_Query_Call{Call: _e.mock.On("Query", + append([]interface{}{name}, args...)...)} +} + +func (_c *Cache_Query_Call) Run(run func(name string, args ...interface{})) *Cache_Query_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]interface{}, len(args)-1) + for i, a := range args[1:] { + if a != nil { + variadicArgs[i] = a.(interface{}) + } + } + run(args[0].(string), variadicArgs...) + }) + return _c +} + +func (_c *Cache_Query_Call) Return(_a0 cache.ResourceIterator, _a1 error) *Cache_Query_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Cache_Query_Call) RunAndReturn(run func(string, ...interface{}) (cache.ResourceIterator, error)) *Cache_Query_Call { + _c.Call.Return(run) + return _c +} + +// NewCache creates a new instance of Cache. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewCache(t interface { + mock.TestingT + Cleanup(func()) +}) *Cache { + mock := &Cache{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/controller/cache/cachemock/mock_Query.go b/internal/controller/cache/cachemock/mock_Query.go new file mode 100644 index 0000000000..3c3311cce9 --- /dev/null +++ b/internal/controller/cache/cachemock/mock_Query.go @@ -0,0 +1,100 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package cachemock + +import ( + cache "github.com/hashicorp/consul/internal/controller/cache" + mock "github.com/stretchr/testify/mock" +) + +// Query is an autogenerated mock type for the Query type +type Query struct { + mock.Mock +} + +type Query_Expecter struct { + mock *mock.Mock +} + +func (_m *Query) EXPECT() *Query_Expecter { + return &Query_Expecter{mock: &_m.Mock} +} + +// Execute provides a mock function with given fields: c, args +func (_m *Query) Execute(c cache.ReadOnlyCache, args ...interface{}) (cache.ResourceIterator, error) { + var _ca []interface{} + _ca = append(_ca, c) + _ca = append(_ca, args...) + ret := _m.Called(_ca...) + + var r0 cache.ResourceIterator + var r1 error + if rf, ok := ret.Get(0).(func(cache.ReadOnlyCache, ...interface{}) (cache.ResourceIterator, error)); ok { + return rf(c, args...) + } + if rf, ok := ret.Get(0).(func(cache.ReadOnlyCache, ...interface{}) cache.ResourceIterator); ok { + r0 = rf(c, args...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(cache.ResourceIterator) + } + } + + if rf, ok := ret.Get(1).(func(cache.ReadOnlyCache, ...interface{}) error); ok { + r1 = rf(c, args...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Query_Execute_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Execute' +type Query_Execute_Call struct { + *mock.Call +} + +// Execute is a helper method to define mock.On call +// - c cache.ReadOnlyCache +// - args ...interface{} +func (_e *Query_Expecter) Execute(c interface{}, args ...interface{}) *Query_Execute_Call { + return &Query_Execute_Call{Call: _e.mock.On("Execute", + append([]interface{}{c}, args...)...)} +} + +func (_c *Query_Execute_Call) Run(run func(c cache.ReadOnlyCache, args ...interface{})) *Query_Execute_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]interface{}, len(args)-1) + for i, a := range args[1:] { + if a != nil { + variadicArgs[i] = a.(interface{}) + } + } + run(args[0].(cache.ReadOnlyCache), variadicArgs...) + }) + return _c +} + +func (_c *Query_Execute_Call) Return(_a0 cache.ResourceIterator, _a1 error) *Query_Execute_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Query_Execute_Call) RunAndReturn(run func(cache.ReadOnlyCache, ...interface{}) (cache.ResourceIterator, error)) *Query_Execute_Call { + _c.Call.Return(run) + return _c +} + +// NewQuery creates a new instance of Query. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewQuery(t interface { + mock.TestingT + Cleanup(func()) +}) *Query { + mock := &Query{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/controller/cache/cachemock/mock_ReadOnlyCache.go b/internal/controller/cache/cachemock/mock_ReadOnlyCache.go new file mode 100644 index 0000000000..8d2f346a5d --- /dev/null +++ b/internal/controller/cache/cachemock/mock_ReadOnlyCache.go @@ -0,0 +1,432 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package cachemock + +import ( + cache "github.com/hashicorp/consul/internal/controller/cache" + mock "github.com/stretchr/testify/mock" + + pbresource "github.com/hashicorp/consul/proto-public/pbresource" +) + +// ReadOnlyCache is an autogenerated mock type for the ReadOnlyCache type +type ReadOnlyCache struct { + mock.Mock +} + +type ReadOnlyCache_Expecter struct { + mock *mock.Mock +} + +func (_m *ReadOnlyCache) EXPECT() *ReadOnlyCache_Expecter { + return &ReadOnlyCache_Expecter{mock: &_m.Mock} +} + +// Get provides a mock function with given fields: it, indexName, args +func (_m *ReadOnlyCache) Get(it *pbresource.Type, indexName string, args ...interface{}) (*pbresource.Resource, error) { + var _ca []interface{} + _ca = append(_ca, it, indexName) + _ca = append(_ca, args...) + ret := _m.Called(_ca...) + + var r0 *pbresource.Resource + var r1 error + if rf, ok := ret.Get(0).(func(*pbresource.Type, string, ...interface{}) (*pbresource.Resource, error)); ok { + return rf(it, indexName, args...) + } + if rf, ok := ret.Get(0).(func(*pbresource.Type, string, ...interface{}) *pbresource.Resource); ok { + r0 = rf(it, indexName, args...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbresource.Resource) + } + } + + if rf, ok := ret.Get(1).(func(*pbresource.Type, string, ...interface{}) error); ok { + r1 = rf(it, indexName, args...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ReadOnlyCache_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get' +type ReadOnlyCache_Get_Call struct { + *mock.Call +} + +// Get is a helper method to define mock.On call +// - it *pbresource.Type +// - indexName string +// - args ...interface{} +func (_e *ReadOnlyCache_Expecter) Get(it interface{}, indexName interface{}, args ...interface{}) *ReadOnlyCache_Get_Call { + return &ReadOnlyCache_Get_Call{Call: _e.mock.On("Get", + append([]interface{}{it, indexName}, args...)...)} +} + +func (_c *ReadOnlyCache_Get_Call) Run(run func(it *pbresource.Type, indexName string, args ...interface{})) *ReadOnlyCache_Get_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]interface{}, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(interface{}) + } + } + run(args[0].(*pbresource.Type), args[1].(string), variadicArgs...) + }) + return _c +} + +func (_c *ReadOnlyCache_Get_Call) Return(_a0 *pbresource.Resource, _a1 error) *ReadOnlyCache_Get_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ReadOnlyCache_Get_Call) RunAndReturn(run func(*pbresource.Type, string, ...interface{}) (*pbresource.Resource, error)) *ReadOnlyCache_Get_Call { + _c.Call.Return(run) + return _c +} + +// List provides a mock function with given fields: it, indexName, args +func (_m *ReadOnlyCache) List(it *pbresource.Type, indexName string, args ...interface{}) ([]*pbresource.Resource, error) { + var _ca []interface{} + _ca = append(_ca, it, indexName) + _ca = append(_ca, args...) + ret := _m.Called(_ca...) + + var r0 []*pbresource.Resource + var r1 error + if rf, ok := ret.Get(0).(func(*pbresource.Type, string, ...interface{}) ([]*pbresource.Resource, error)); ok { + return rf(it, indexName, args...) + } + if rf, ok := ret.Get(0).(func(*pbresource.Type, string, ...interface{}) []*pbresource.Resource); ok { + r0 = rf(it, indexName, args...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*pbresource.Resource) + } + } + + if rf, ok := ret.Get(1).(func(*pbresource.Type, string, ...interface{}) error); ok { + r1 = rf(it, indexName, args...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ReadOnlyCache_List_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'List' +type ReadOnlyCache_List_Call struct { + *mock.Call +} + +// List is a helper method to define mock.On call +// - it *pbresource.Type +// - indexName string +// - args ...interface{} +func (_e *ReadOnlyCache_Expecter) List(it interface{}, indexName interface{}, args ...interface{}) *ReadOnlyCache_List_Call { + return &ReadOnlyCache_List_Call{Call: _e.mock.On("List", + append([]interface{}{it, indexName}, args...)...)} +} + +func (_c *ReadOnlyCache_List_Call) Run(run func(it *pbresource.Type, indexName string, args ...interface{})) *ReadOnlyCache_List_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]interface{}, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(interface{}) + } + } + run(args[0].(*pbresource.Type), args[1].(string), variadicArgs...) + }) + return _c +} + +func (_c *ReadOnlyCache_List_Call) Return(_a0 []*pbresource.Resource, _a1 error) *ReadOnlyCache_List_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ReadOnlyCache_List_Call) RunAndReturn(run func(*pbresource.Type, string, ...interface{}) ([]*pbresource.Resource, error)) *ReadOnlyCache_List_Call { + _c.Call.Return(run) + return _c +} + +// ListIterator provides a mock function with given fields: it, indexName, args +func (_m *ReadOnlyCache) ListIterator(it *pbresource.Type, indexName string, args ...interface{}) (cache.ResourceIterator, error) { + var _ca []interface{} + _ca = append(_ca, it, indexName) + _ca = append(_ca, args...) + ret := _m.Called(_ca...) + + var r0 cache.ResourceIterator + var r1 error + if rf, ok := ret.Get(0).(func(*pbresource.Type, string, ...interface{}) (cache.ResourceIterator, error)); ok { + return rf(it, indexName, args...) + } + if rf, ok := ret.Get(0).(func(*pbresource.Type, string, ...interface{}) cache.ResourceIterator); ok { + r0 = rf(it, indexName, args...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(cache.ResourceIterator) + } + } + + if rf, ok := ret.Get(1).(func(*pbresource.Type, string, ...interface{}) error); ok { + r1 = rf(it, indexName, args...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ReadOnlyCache_ListIterator_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListIterator' +type ReadOnlyCache_ListIterator_Call struct { + *mock.Call +} + +// ListIterator is a helper method to define mock.On call +// - it *pbresource.Type +// - indexName string +// - args ...interface{} +func (_e *ReadOnlyCache_Expecter) ListIterator(it interface{}, indexName interface{}, args ...interface{}) *ReadOnlyCache_ListIterator_Call { + return &ReadOnlyCache_ListIterator_Call{Call: _e.mock.On("ListIterator", + append([]interface{}{it, indexName}, args...)...)} +} + +func (_c *ReadOnlyCache_ListIterator_Call) Run(run func(it *pbresource.Type, indexName string, args ...interface{})) *ReadOnlyCache_ListIterator_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]interface{}, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(interface{}) + } + } + run(args[0].(*pbresource.Type), args[1].(string), variadicArgs...) + }) + return _c +} + +func (_c *ReadOnlyCache_ListIterator_Call) Return(_a0 cache.ResourceIterator, _a1 error) *ReadOnlyCache_ListIterator_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ReadOnlyCache_ListIterator_Call) RunAndReturn(run func(*pbresource.Type, string, ...interface{}) (cache.ResourceIterator, error)) *ReadOnlyCache_ListIterator_Call { + _c.Call.Return(run) + return _c +} + +// Parents provides a mock function with given fields: it, indexName, args +func (_m *ReadOnlyCache) Parents(it *pbresource.Type, indexName string, args ...interface{}) ([]*pbresource.Resource, error) { + var _ca []interface{} + _ca = append(_ca, it, indexName) + _ca = append(_ca, args...) + ret := _m.Called(_ca...) + + var r0 []*pbresource.Resource + var r1 error + if rf, ok := ret.Get(0).(func(*pbresource.Type, string, ...interface{}) ([]*pbresource.Resource, error)); ok { + return rf(it, indexName, args...) + } + if rf, ok := ret.Get(0).(func(*pbresource.Type, string, ...interface{}) []*pbresource.Resource); ok { + r0 = rf(it, indexName, args...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*pbresource.Resource) + } + } + + if rf, ok := ret.Get(1).(func(*pbresource.Type, string, ...interface{}) error); ok { + r1 = rf(it, indexName, args...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ReadOnlyCache_Parents_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Parents' +type ReadOnlyCache_Parents_Call struct { + *mock.Call +} + +// Parents is a helper method to define mock.On call +// - it *pbresource.Type +// - indexName string +// - args ...interface{} +func (_e *ReadOnlyCache_Expecter) Parents(it interface{}, indexName interface{}, args ...interface{}) *ReadOnlyCache_Parents_Call { + return &ReadOnlyCache_Parents_Call{Call: _e.mock.On("Parents", + append([]interface{}{it, indexName}, args...)...)} +} + +func (_c *ReadOnlyCache_Parents_Call) Run(run func(it *pbresource.Type, indexName string, args ...interface{})) *ReadOnlyCache_Parents_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]interface{}, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(interface{}) + } + } + run(args[0].(*pbresource.Type), args[1].(string), variadicArgs...) + }) + return _c +} + +func (_c *ReadOnlyCache_Parents_Call) Return(_a0 []*pbresource.Resource, _a1 error) *ReadOnlyCache_Parents_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ReadOnlyCache_Parents_Call) RunAndReturn(run func(*pbresource.Type, string, ...interface{}) ([]*pbresource.Resource, error)) *ReadOnlyCache_Parents_Call { + _c.Call.Return(run) + return _c +} + +// ParentsIterator provides a mock function with given fields: it, indexName, args +func (_m *ReadOnlyCache) ParentsIterator(it *pbresource.Type, indexName string, args ...interface{}) (cache.ResourceIterator, error) { + var _ca []interface{} + _ca = append(_ca, it, indexName) + _ca = append(_ca, args...) + ret := _m.Called(_ca...) + + var r0 cache.ResourceIterator + var r1 error + if rf, ok := ret.Get(0).(func(*pbresource.Type, string, ...interface{}) (cache.ResourceIterator, error)); ok { + return rf(it, indexName, args...) + } + if rf, ok := ret.Get(0).(func(*pbresource.Type, string, ...interface{}) cache.ResourceIterator); ok { + r0 = rf(it, indexName, args...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(cache.ResourceIterator) + } + } + + if rf, ok := ret.Get(1).(func(*pbresource.Type, string, ...interface{}) error); ok { + r1 = rf(it, indexName, args...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ReadOnlyCache_ParentsIterator_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ParentsIterator' +type ReadOnlyCache_ParentsIterator_Call struct { + *mock.Call +} + +// ParentsIterator is a helper method to define mock.On call +// - it *pbresource.Type +// - indexName string +// - args ...interface{} +func (_e *ReadOnlyCache_Expecter) ParentsIterator(it interface{}, indexName interface{}, args ...interface{}) *ReadOnlyCache_ParentsIterator_Call { + return &ReadOnlyCache_ParentsIterator_Call{Call: _e.mock.On("ParentsIterator", + append([]interface{}{it, indexName}, args...)...)} +} + +func (_c *ReadOnlyCache_ParentsIterator_Call) Run(run func(it *pbresource.Type, indexName string, args ...interface{})) *ReadOnlyCache_ParentsIterator_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]interface{}, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(interface{}) + } + } + run(args[0].(*pbresource.Type), args[1].(string), variadicArgs...) + }) + return _c +} + +func (_c *ReadOnlyCache_ParentsIterator_Call) Return(_a0 cache.ResourceIterator, _a1 error) *ReadOnlyCache_ParentsIterator_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ReadOnlyCache_ParentsIterator_Call) RunAndReturn(run func(*pbresource.Type, string, ...interface{}) (cache.ResourceIterator, error)) *ReadOnlyCache_ParentsIterator_Call { + _c.Call.Return(run) + return _c +} + +// Query provides a mock function with given fields: name, args +func (_m *ReadOnlyCache) Query(name string, args ...interface{}) (cache.ResourceIterator, error) { + var _ca []interface{} + _ca = append(_ca, name) + _ca = append(_ca, args...) + ret := _m.Called(_ca...) + + var r0 cache.ResourceIterator + var r1 error + if rf, ok := ret.Get(0).(func(string, ...interface{}) (cache.ResourceIterator, error)); ok { + return rf(name, args...) + } + if rf, ok := ret.Get(0).(func(string, ...interface{}) cache.ResourceIterator); ok { + r0 = rf(name, args...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(cache.ResourceIterator) + } + } + + if rf, ok := ret.Get(1).(func(string, ...interface{}) error); ok { + r1 = rf(name, args...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ReadOnlyCache_Query_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Query' +type ReadOnlyCache_Query_Call struct { + *mock.Call +} + +// Query is a helper method to define mock.On call +// - name string +// - args ...interface{} +func (_e *ReadOnlyCache_Expecter) Query(name interface{}, args ...interface{}) *ReadOnlyCache_Query_Call { + return &ReadOnlyCache_Query_Call{Call: _e.mock.On("Query", + append([]interface{}{name}, args...)...)} +} + +func (_c *ReadOnlyCache_Query_Call) Run(run func(name string, args ...interface{})) *ReadOnlyCache_Query_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]interface{}, len(args)-1) + for i, a := range args[1:] { + if a != nil { + variadicArgs[i] = a.(interface{}) + } + } + run(args[0].(string), variadicArgs...) + }) + return _c +} + +func (_c *ReadOnlyCache_Query_Call) Return(_a0 cache.ResourceIterator, _a1 error) *ReadOnlyCache_Query_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ReadOnlyCache_Query_Call) RunAndReturn(run func(string, ...interface{}) (cache.ResourceIterator, error)) *ReadOnlyCache_Query_Call { + _c.Call.Return(run) + return _c +} + +// NewReadOnlyCache creates a new instance of ReadOnlyCache. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewReadOnlyCache(t interface { + mock.TestingT + Cleanup(func()) +}) *ReadOnlyCache { + mock := &ReadOnlyCache{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/controller/cache/cachemock/mock_ResourceIterator.go b/internal/controller/cache/cachemock/mock_ResourceIterator.go new file mode 100644 index 0000000000..63178fcdf3 --- /dev/null +++ b/internal/controller/cache/cachemock/mock_ResourceIterator.go @@ -0,0 +1,78 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package cachemock + +import ( + pbresource "github.com/hashicorp/consul/proto-public/pbresource" + mock "github.com/stretchr/testify/mock" +) + +// ResourceIterator is an autogenerated mock type for the ResourceIterator type +type ResourceIterator struct { + mock.Mock +} + +type ResourceIterator_Expecter struct { + mock *mock.Mock +} + +func (_m *ResourceIterator) EXPECT() *ResourceIterator_Expecter { + return &ResourceIterator_Expecter{mock: &_m.Mock} +} + +// Next provides a mock function with given fields: +func (_m *ResourceIterator) Next() *pbresource.Resource { + ret := _m.Called() + + var r0 *pbresource.Resource + if rf, ok := ret.Get(0).(func() *pbresource.Resource); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbresource.Resource) + } + } + + return r0 +} + +// ResourceIterator_Next_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Next' +type ResourceIterator_Next_Call struct { + *mock.Call +} + +// Next is a helper method to define mock.On call +func (_e *ResourceIterator_Expecter) Next() *ResourceIterator_Next_Call { + return &ResourceIterator_Next_Call{Call: _e.mock.On("Next")} +} + +func (_c *ResourceIterator_Next_Call) Run(run func()) *ResourceIterator_Next_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ResourceIterator_Next_Call) Return(_a0 *pbresource.Resource) *ResourceIterator_Next_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ResourceIterator_Next_Call) RunAndReturn(run func() *pbresource.Resource) *ResourceIterator_Next_Call { + _c.Call.Return(run) + return _c +} + +// NewResourceIterator creates a new instance of ResourceIterator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewResourceIterator(t interface { + mock.TestingT + Cleanup(func()) +}) *ResourceIterator { + mock := &ResourceIterator{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/controller/cache/cachemock/mock_WriteCache.go b/internal/controller/cache/cachemock/mock_WriteCache.go new file mode 100644 index 0000000000..ef51b380d8 --- /dev/null +++ b/internal/controller/cache/cachemock/mock_WriteCache.go @@ -0,0 +1,119 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package cachemock + +import ( + pbresource "github.com/hashicorp/consul/proto-public/pbresource" + mock "github.com/stretchr/testify/mock" +) + +// WriteCache is an autogenerated mock type for the WriteCache type +type WriteCache struct { + mock.Mock +} + +type WriteCache_Expecter struct { + mock *mock.Mock +} + +func (_m *WriteCache) EXPECT() *WriteCache_Expecter { + return &WriteCache_Expecter{mock: &_m.Mock} +} + +// Delete provides a mock function with given fields: r +func (_m *WriteCache) Delete(r *pbresource.Resource) error { + ret := _m.Called(r) + + var r0 error + if rf, ok := ret.Get(0).(func(*pbresource.Resource) error); ok { + r0 = rf(r) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// WriteCache_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete' +type WriteCache_Delete_Call struct { + *mock.Call +} + +// Delete is a helper method to define mock.On call +// - r *pbresource.Resource +func (_e *WriteCache_Expecter) Delete(r interface{}) *WriteCache_Delete_Call { + return &WriteCache_Delete_Call{Call: _e.mock.On("Delete", r)} +} + +func (_c *WriteCache_Delete_Call) Run(run func(r *pbresource.Resource)) *WriteCache_Delete_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*pbresource.Resource)) + }) + return _c +} + +func (_c *WriteCache_Delete_Call) Return(_a0 error) *WriteCache_Delete_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *WriteCache_Delete_Call) RunAndReturn(run func(*pbresource.Resource) error) *WriteCache_Delete_Call { + _c.Call.Return(run) + return _c +} + +// Insert provides a mock function with given fields: r +func (_m *WriteCache) Insert(r *pbresource.Resource) error { + ret := _m.Called(r) + + var r0 error + if rf, ok := ret.Get(0).(func(*pbresource.Resource) error); ok { + r0 = rf(r) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// WriteCache_Insert_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Insert' +type WriteCache_Insert_Call struct { + *mock.Call +} + +// Insert is a helper method to define mock.On call +// - r *pbresource.Resource +func (_e *WriteCache_Expecter) Insert(r interface{}) *WriteCache_Insert_Call { + return &WriteCache_Insert_Call{Call: _e.mock.On("Insert", r)} +} + +func (_c *WriteCache_Insert_Call) Run(run func(r *pbresource.Resource)) *WriteCache_Insert_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*pbresource.Resource)) + }) + return _c +} + +func (_c *WriteCache_Insert_Call) Return(_a0 error) *WriteCache_Insert_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *WriteCache_Insert_Call) RunAndReturn(run func(*pbresource.Resource) error) *WriteCache_Insert_Call { + _c.Call.Return(run) + return _c +} + +// NewWriteCache creates a new instance of WriteCache. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewWriteCache(t interface { + mock.TestingT + Cleanup(func()) +}) *WriteCache { + mock := &WriteCache{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/controller/cache/client.go b/internal/controller/cache/client.go new file mode 100644 index 0000000000..df37c7381c --- /dev/null +++ b/internal/controller/cache/client.go @@ -0,0 +1,56 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package cache + +import ( + "context" + + "github.com/hashicorp/consul/proto-public/pbresource" + "google.golang.org/grpc" +) + +type writeThroughCacheClient struct { + cache WriteCache + pbresource.ResourceServiceClient +} + +func NewCachedClient(cache WriteCache, client pbresource.ResourceServiceClient) pbresource.ResourceServiceClient { + return &writeThroughCacheClient{ + cache: cache, + ResourceServiceClient: client, + } +} + +func (c *writeThroughCacheClient) Write(ctx context.Context, in *pbresource.WriteRequest, opts ...grpc.CallOption) (*pbresource.WriteResponse, error) { + rsp, err := c.ResourceServiceClient.Write(ctx, in, opts...) + if err != nil { + return rsp, err + } + + // There was no error so insert the resource into the cache + c.cache.Insert(rsp.Resource) + return rsp, err +} + +func (c *writeThroughCacheClient) WriteStatus(ctx context.Context, in *pbresource.WriteStatusRequest, opts ...grpc.CallOption) (*pbresource.WriteStatusResponse, error) { + rsp, err := c.ResourceServiceClient.WriteStatus(ctx, in, opts...) + if err != nil { + return rsp, err + } + + // There was no error so insert the resource into the cache + c.cache.Insert(rsp.Resource) + return rsp, err +} + +func (c *writeThroughCacheClient) Delete(ctx context.Context, in *pbresource.DeleteRequest, opts ...grpc.CallOption) (*pbresource.DeleteResponse, error) { + rsp, err := c.ResourceServiceClient.Delete(ctx, in, opts...) + if err != nil { + return rsp, err + } + + // There was no error so delete the resource from the cache + c.cache.Delete(&pbresource.Resource{Id: in.Id}) + return rsp, err +} diff --git a/internal/controller/cache/client_test.go b/internal/controller/cache/client_test.go new file mode 100644 index 0000000000..9e167a84bf --- /dev/null +++ b/internal/controller/cache/client_test.go @@ -0,0 +1,265 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package cache + +import ( + "context" + "testing" + + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + mockpbresource "github.com/hashicorp/consul/grpcmocks/proto-public/pbresource" + "github.com/hashicorp/consul/internal/resource" + "github.com/hashicorp/consul/internal/resource/resourcetest" + "github.com/hashicorp/consul/proto-public/pbresource" + pbdemo "github.com/hashicorp/consul/proto/private/pbdemo/v1" + "github.com/hashicorp/consul/proto/private/prototest" +) + +type cacheClientSuite struct { + suite.Suite + + cache Cache + mclient *mockpbresource.ResourceServiceClient_Expecter + client pbresource.ResourceServiceClient + + album1 *pbresource.Resource + album2 *pbresource.Resource +} + +func (suite *cacheClientSuite) SetupTest() { + suite.cache = New() + + // It would be difficult to use the inmem resource service here due to cyclical dependencies. + // Any type registrations from other packages cannot be imported because those packages + // will require the controller package which will require this cache package. The easiest + // way of getting around this was to not use the real resource service and require type registrations. + client := mockpbresource.NewResourceServiceClient(suite.T()) + suite.mclient = client.EXPECT() + + require.NoError(suite.T(), suite.cache.AddIndex(pbdemo.AlbumType, namePrefixIndexer())) + require.NoError(suite.T(), suite.cache.AddIndex(pbdemo.AlbumType, releaseYearIndexer())) + require.NoError(suite.T(), suite.cache.AddIndex(pbdemo.AlbumType, tracksIndexer())) + + suite.album1 = resourcetest.Resource(pbdemo.AlbumType, "one"). + WithTenancy(resource.DefaultNamespacedTenancy()). + WithData(suite.T(), &pbdemo.Album{ + Name: "one", + YearOfRelease: 2023, + Tracks: []string{"foo", "bar", "baz"}, + }). + Build() + + suite.album2 = resourcetest.Resource(pbdemo.AlbumType, "two"). + WithTenancy(resource.DefaultNamespacedTenancy()). + WithData(suite.T(), &pbdemo.Album{ + Name: "two", + YearOfRelease: 2023, + Tracks: []string{"fangorn", "zoo"}, + }). + Build() + + suite.cache.Insert(suite.album1) + suite.cache.Insert(suite.album2) + + suite.client = NewCachedClient(suite.cache, client) +} + +func (suite *cacheClientSuite) performWrite(res *pbresource.Resource, shouldError bool) { + req := &pbresource.WriteRequest{ + Resource: res, + } + + // Setup the expectation for the inner mocked client to receive the real request + if shouldError { + suite.mclient.Write(mock.Anything, req). + Return(nil, fakeWrappedErr). + Once() + } else { + suite.mclient.Write(mock.Anything, req). + Return(&pbresource.WriteResponse{ + Resource: res, + }, nil). + Once() + } + + // Now use the wrapper client to perform the request + out, err := suite.client.Write(context.Background(), req) + if shouldError { + require.ErrorIs(suite.T(), err, fakeWrappedErr) + require.Nil(suite.T(), out) + } else { + require.NoError(suite.T(), err) + prototest.AssertDeepEqual(suite.T(), res, out.Resource) + } +} + +func (suite *cacheClientSuite) performDelete(id *pbresource.ID, shouldError bool) { + req := &pbresource.DeleteRequest{ + Id: id, + } + + // Setup the expectation for the inner mocked client to receive the real request + if shouldError { + suite.mclient.Delete(mock.Anything, req). + Return(nil, fakeWrappedErr). + Once() + } else { + suite.mclient.Delete(mock.Anything, req). + Return(&pbresource.DeleteResponse{}, nil). + Once() + } + + // Now use the wrapper client to perform the request + out, err := suite.client.Delete(context.Background(), req) + if shouldError { + require.ErrorIs(suite.T(), err, fakeWrappedErr) + require.Nil(suite.T(), out) + } else { + require.NoError(suite.T(), err) + require.NotNil(suite.T(), out) + } +} + +func (suite *cacheClientSuite) performWriteStatus(res *pbresource.Resource, key string, status *pbresource.Status, shouldError bool) { + req := &pbresource.WriteStatusRequest{ + Id: res.Id, + Key: key, + Status: status, + } + + // Setup the expectation for the inner mocked client to receive the real request + if shouldError { + suite.mclient.WriteStatus(mock.Anything, req). + Return(nil, fakeWrappedErr). + Once() + } else { + suite.mclient.WriteStatus(mock.Anything, req). + Return(&pbresource.WriteStatusResponse{ + Resource: res, + }, nil). + Once() + } + + // Now use the wrapper client to perform the request + out, err := suite.client.WriteStatus(context.Background(), req) + if shouldError { + require.ErrorIs(suite.T(), err, fakeWrappedErr) + require.Nil(suite.T(), out) + } else { + require.NoError(suite.T(), err) + prototest.AssertDeepEqual(suite.T(), res, out.Resource) + } +} + +func (suite *cacheClientSuite) TestWrite_Ok() { + newRes := resourcetest.ResourceID(suite.album1.Id). + WithTenancy(&pbresource.Tenancy{ + Partition: "default", + Namespace: "default", + }). + WithData(suite.T(), &pbdemo.Album{ + Name: "changed", + YearOfRelease: 2023, + Tracks: []string{"fangorn", "zoo"}, + }). + Build() + + suite.performWrite(newRes, false) + + // now ensure the entry was updated in the cache + res, err := suite.cache.Get(suite.album1.Id.Type, "id", suite.album1.Id) + require.NoError(suite.T(), err) + require.NotNil(suite.T(), res) + prototest.AssertDeepEqual(suite.T(), newRes, res) +} + +func (suite *cacheClientSuite) TestWrite_Error() { + newRes := resourcetest.ResourceID(suite.album1.Id). + WithData(suite.T(), &pbdemo.Album{ + Name: "changed", + YearOfRelease: 2023, + Tracks: []string{"fangorn", "zoo"}, + }). + WithVersion("notaversion"). + Build() + + suite.performWrite(newRes, true) + + // now ensure the entry was not updated in the cache + res, err := suite.cache.Get(suite.album1.Id.Type, "id", suite.album1.Id) + require.NoError(suite.T(), err) + require.NotNil(suite.T(), res) + prototest.AssertDeepEqual(suite.T(), suite.album1, res) +} + +func (suite *cacheClientSuite) TestWriteStatus_Ok() { + status := &pbresource.Status{ObservedGeneration: suite.album1.Generation} + + updatedRes := resourcetest.ResourceID(suite.album1.Id). + WithData(suite.T(), &pbdemo.Album{ + Name: "changed", + YearOfRelease: 2023, + Tracks: []string{"fangorn", "zoo"}, + }). + WithStatus("testing", status). + WithVersion("notaversion"). + Build() + + suite.performWriteStatus(updatedRes, "testing", status, false) + + // now ensure the entry was updated in the cache + res, err := suite.cache.Get(suite.album1.Id.Type, "id", suite.album1.Id) + require.NoError(suite.T(), err) + require.NotNil(suite.T(), res) + _, updated := res.Status["testing"] + require.True(suite.T(), updated) +} + +func (suite *cacheClientSuite) TestWriteStatus_Error() { + status := &pbresource.Status{ObservedGeneration: suite.album1.Generation} + + updatedRes := resourcetest.ResourceID(suite.album1.Id). + WithData(suite.T(), &pbdemo.Album{ + Name: "changed", + YearOfRelease: 2023, + Tracks: []string{"fangorn", "zoo"}, + }). + WithStatus("testing", status). + WithVersion("notaversion"). + Build() + + suite.performWriteStatus(updatedRes, "testing", status, true) + + // now ensure the entry was not updated in the cache + res, err := suite.cache.Get(suite.album1.Id.Type, "id", suite.album1.Id) + require.NoError(suite.T(), err) + require.NotNil(suite.T(), res) + _, updated := res.Status["testing"] + require.False(suite.T(), updated) +} + +func (suite *cacheClientSuite) TestDelete_Ok() { + suite.performDelete(suite.album1.Id, false) + + // now ensure the entry was removed from the cache + res, err := suite.cache.Get(suite.album1.Id.Type, "id", suite.album1.Id) + require.NoError(suite.T(), err) + require.Nil(suite.T(), res) +} + +func (suite *cacheClientSuite) TestDelete_Error() { + suite.performDelete(suite.album1.Id, true) + + // now ensure the entry was NOT removed from the cache + res, err := suite.cache.Get(suite.album1.Id.Type, "id", suite.album1.Id) + require.NoError(suite.T(), err) + require.NotNil(suite.T(), res) +} + +func TestCacheClient(t *testing.T) { + suite.Run(t, new(cacheClientSuite)) +} diff --git a/internal/controller/cache/errors.go b/internal/controller/cache/errors.go new file mode 100644 index 0000000000..e269b4755a --- /dev/null +++ b/internal/controller/cache/errors.go @@ -0,0 +1,73 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package cache + +import ( + "errors" + "fmt" +) + +var ( + QueryRequired = errors.New("A non-nil query function was not specified") + TypeUnspecifiedError = errors.New("the resource type was not specified") + TypeNotIndexedError = errors.New("the resource type specified is not indexed") +) + +type QueryNotFoundError struct { + name string +} + +func (e QueryNotFoundError) Error() string { + return fmt.Sprintf("No query with name %q exists", e.name) +} + +type IndexNotFoundError struct { + name string +} + +func (e IndexNotFoundError) Error() string { + return fmt.Sprintf("No index with name %q exists", e.name) +} + +type CacheTypeError struct { + err error + it unversionedType +} + +func (e CacheTypeError) Error() string { + return fmt.Sprintf("operation on resource type %s.%s failed: %v", e.it.Group, e.it.Kind, e.err.Error()) +} + +func (e CacheTypeError) Unwrap() error { + return e.err +} + +type IndexError struct { + err error + name string +} + +func (e IndexError) Error() string { + return fmt.Sprintf("operation on index %q failed: %v", e.name, e.err.Error()) +} + +func (e IndexError) Unwrap() error { + return e.err +} + +type DuplicateIndexError struct { + name string +} + +func (e DuplicateIndexError) Error() string { + return fmt.Sprintf("Index with name %q is already defined.", e.name) +} + +type DuplicateQueryError struct { + name string +} + +func (e DuplicateQueryError) Error() string { + return fmt.Sprintf("Query with name %q is already defined.", e.name) +} diff --git a/internal/controller/cache/errors_test.go b/internal/controller/cache/errors_test.go new file mode 100644 index 0000000000..af5f660d4e --- /dev/null +++ b/internal/controller/cache/errors_test.go @@ -0,0 +1,59 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package cache + +import ( + "fmt" + "testing" + + "github.com/hashicorp/consul/internal/testing/errors" +) + +var ( + fakeWrappedErr = fmt.Errorf("fake test error") +) + +func TestErrorStrings(t *testing.T) { + errors.TestErrorStrings(t, map[string]error{ + "IndexNotFound": IndexNotFoundError{name: "fake"}, + "QueryNotFound": QueryNotFoundError{name: "fake"}, + "QueryRequired": QueryRequired, + "CacheTypeError": CacheTypeError{ + err: fakeWrappedErr, + it: unversionedType{ + Group: "something", + Kind: "else", + }, + }, + "IndexError": IndexError{ + err: fakeWrappedErr, + name: "foo", + }, + "DuplicateIndexError": DuplicateIndexError{ + name: "addresses", + }, + "DuplicateQueryError": DuplicateQueryError{ + name: "addresses", + }, + }) +} + +func TestErrorUnwrap(t *testing.T) { + errors.TestErrorUnwrap(t, map[string]errors.UnwrapErrorTestCase{ + "IndexError": { + Err: IndexError{ + name: "blah", + err: fakeWrappedErr, + }, + Expected: fakeWrappedErr, + }, + "CacheTypeError": { + Err: CacheTypeError{ + it: unversionedType{Group: "something", Kind: "else"}, + err: fakeWrappedErr, + }, + Expected: fakeWrappedErr, + }, + }) +} diff --git a/internal/controller/cache/index/.mockery.yaml b/internal/controller/cache/index/.mockery.yaml new file mode 100644 index 0000000000..c198bc6036 --- /dev/null +++ b/internal/controller/cache/index/.mockery.yaml @@ -0,0 +1,23 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +with-expecter: true +recursive: false +# We don't want the mocks within proto-public so as to force a dependency +# of the testify library on the modules usage. The mocks are only for +# internal testing purposes. Other consumers can generated the mocks into +# their own code base. +dir: "indexmock" +outpkg: "indexmock" +# camelcase ensure that private interfaces also get public mocks +mockname: "{{.InterfaceName | camelcase }}" + +# The Txn and IndexOption interfaces require this package and if the mocks +# are generated out of package (in the indexmock package) then it will +# cause there to be cyclical imports. As we don't need mocks for these +# right now it is easier to just exclude them. +include-regex: ".*" +exclude-regex: "^(Txn|IndexOption)$" + +packages: + github.com/hashicorp/consul/internal/controller/cache/index: diff --git a/internal/controller/cache/index/builder.go b/internal/controller/cache/index/builder.go new file mode 100644 index 0000000000..16841a722c --- /dev/null +++ b/internal/controller/cache/index/builder.go @@ -0,0 +1,33 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package index + +import ( + "bytes" +) + +// indexSeparator delimits the segments of our radix tree keys. +const indexSeparator = "\x00" + +type Builder bytes.Buffer + +func (i *Builder) Raw(v []byte) { + (*bytes.Buffer)(i).Write(v) +} + +func (i *Builder) String(s string) { + (*bytes.Buffer)(i).WriteString(s) + (*bytes.Buffer)(i).WriteString(indexSeparator) +} + +func (i *Builder) Bytes() []byte { + return (*bytes.Buffer)(i).Bytes() +} + +func (i *Builder) Write(b []byte) (int, error) { + (*bytes.Buffer)(i).Write(b) + (*bytes.Buffer)(i).WriteString(indexSeparator) + + return len(b), nil +} diff --git a/internal/controller/cache/index/builder_test.go b/internal/controller/cache/index/builder_test.go new file mode 100644 index 0000000000..754c16f62f --- /dev/null +++ b/internal/controller/cache/index/builder_test.go @@ -0,0 +1,33 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package index + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestBuilderRaw(t *testing.T) { + var b Builder + b.Raw([]byte{1, 2, 3}) + + require.Equal(t, []byte{1, 2, 3}, b.Bytes()) +} + +func TestBuilderString(t *testing.T) { + var b Builder + b.String("abc") + + // Ensure that the null terminator is tacked on + require.Equal(t, []byte{'a', 'b', 'c', 0}, b.Bytes()) +} + +func TestBuilderWrite(t *testing.T) { + var b Builder + wrote, err := b.Write([]byte{1, 2, 3}) + require.NoError(t, err) + require.Equal(t, 3, wrote) + require.Equal(t, []byte{1, 2, 3, 0}, b.Bytes()) +} diff --git a/internal/controller/cache/index/convenience.go b/internal/controller/cache/index/convenience.go new file mode 100644 index 0000000000..fded6930a0 --- /dev/null +++ b/internal/controller/cache/index/convenience.go @@ -0,0 +1,132 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package index + +import ( + "fmt" + + "github.com/hashicorp/consul/internal/resource" + "github.com/hashicorp/consul/internal/storage" + "github.com/hashicorp/consul/proto-public/pbresource" +) + +func IndexFromID(id *pbresource.ID, includeUid bool) []byte { + var b Builder + b.Raw(IndexFromType(id.Type)) + b.Raw(IndexFromTenancy(id.Tenancy)) + b.String(id.Name) + if includeUid { + b.String(id.Uid) + } + return b.Bytes() +} + +func IndexFromRefOrID(ref resource.ReferenceOrID) []byte { + var b Builder + b.Raw(IndexFromType(ref.GetType())) + b.Raw(IndexFromTenancy(ref.GetTenancy())) + b.String(ref.GetName()) + return b.Bytes() +} + +func PrefixIndexFromRefOrID(ref resource.ReferenceOrID) []byte { + var b Builder + b.Raw(IndexFromType(ref.GetType())) + + raw, done := prefixIndexFromTenancy(ref.GetTenancy()) + b.Raw(raw) + + if done { + return b.Bytes() + } + + b.Raw([]byte(ref.GetName())) + return b.Bytes() +} + +func prefixIndexFromTenancy(t *pbresource.Tenancy) ([]byte, bool) { + var b Builder + partition := t.GetPartition() + if partition == "" || partition == storage.Wildcard { + return b.Bytes(), true + } + + b.String(partition) + + namespace := t.GetNamespace() + + if namespace == "" || namespace == storage.Wildcard { + return b.Bytes(), true + } + + b.String(namespace) + + return b.Bytes(), false +} + +func IndexFromType(t *pbresource.Type) []byte { + var b Builder + b.String(t.Group) + b.String(t.Kind) + return b.Bytes() +} + +func IndexFromTenancy(t *pbresource.Tenancy) []byte { + var b Builder + b.String(t.GetPartition()) + b.String(t.GetNamespace()) + return b.Bytes() +} + +var ReferenceOrIDFromArgs = SingleValueFromArgs[resource.ReferenceOrID](func(r resource.ReferenceOrID) ([]byte, error) { + return IndexFromRefOrID(r), nil +}) + +var PrefixReferenceOrIDFromArgs = SingleValueFromArgs[resource.ReferenceOrID](func(r resource.ReferenceOrID) ([]byte, error) { + return PrefixIndexFromRefOrID(r), nil +}) + +func SingleValueFromArgs[T any](indexer func(value T) ([]byte, error)) func(args ...any) ([]byte, error) { + return func(args ...any) ([]byte, error) { + var zero T + if l := len(args); l != 1 { + return nil, fmt.Errorf("expected 1 arg, got: %d", l) + } + + value, ok := args[0].(T) + if !ok { + return nil, fmt.Errorf("expected %T, got: %T", zero, args[0]) + } + + return indexer(value) + } +} + +func SingleValueFromOneOrTwoArgs[T1 any, T2 any](indexer func(value T1, optional T2) ([]byte, error)) func(args ...any) ([]byte, error) { + return func(args ...any) ([]byte, error) { + var value T1 + var optional T2 + + l := len(args) + switch l { + case 2: + val, ok := args[1].(T2) + if !ok { + return nil, fmt.Errorf("expected second argument type of %T, got: %T", optional, args[1]) + } + optional = val + fallthrough + case 1: + val, ok := args[0].(T1) + if !ok { + return nil, fmt.Errorf("expected first argument type of %T, got: %T", value, args[1]) + } + value = val + default: + return nil, fmt.Errorf("expected 1 or 2 args, got: %d", l) + } + + return indexer(value, optional) + } +} diff --git a/internal/controller/cache/index/convenience_test.go b/internal/controller/cache/index/convenience_test.go new file mode 100644 index 0000000000..adb0f3a8e2 --- /dev/null +++ b/internal/controller/cache/index/convenience_test.go @@ -0,0 +1,334 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package index + +import ( + "errors" + "testing" + + "github.com/hashicorp/consul/internal/resource" + "github.com/hashicorp/consul/internal/storage" + "github.com/hashicorp/consul/proto-public/pbresource" + "github.com/stretchr/testify/require" +) + +func TestIndexFromID(t *testing.T) { + id := &pbresource.ID{ + Type: &pbresource.Type{ + Group: "test", + GroupVersion: "v1", + Kind: "fake", + }, + Tenancy: &pbresource.Tenancy{ + Partition: "foo", + Namespace: "bar", + }, + Name: "baz", + Uid: "01G9R78VJ5VXAJRA2K7V0RXB87", + } + + val := IndexFromID(id, false) + expected := []byte("test\x00fake\x00foo\x00bar\x00baz\x00") + require.Equal(t, expected, val) + + val = IndexFromID(id, true) + expected = []byte("test\x00fake\x00foo\x00bar\x00baz\x0001G9R78VJ5VXAJRA2K7V0RXB87\x00") + require.Equal(t, expected, val) +} + +func TestIndexFromRefOrID(t *testing.T) { + id := &pbresource.ID{ + Type: &pbresource.Type{ + Group: "test", + GroupVersion: "v1", + Kind: "fake", + }, + Tenancy: &pbresource.Tenancy{ + Partition: "foo", + Namespace: "bar", + }, + Name: "baz", + Uid: "01G9R78VJ5VXAJRA2K7V0RXB87", + } + + ref := resource.Reference(id, "") + + idVal := IndexFromRefOrID(id) + refVal := IndexFromRefOrID(ref) + + expected := "test\x00fake\x00foo\x00bar\x00baz\x00" + require.Equal(t, []byte(expected), idVal) + require.Equal(t, []byte(expected), refVal) +} + +func TestPrefixIndexFromRefOrID(t *testing.T) { + t.Run("no partition", func(t *testing.T) { + ref := &pbresource.Reference{ + Type: &pbresource.Type{ + Group: "test", + GroupVersion: "v1", + Kind: "fake", + }, + } + + require.Equal(t, []byte("test\x00fake\x00"), PrefixIndexFromRefOrID(ref)) + }) + + t.Run("no namespace", func(t *testing.T) { + ref := &pbresource.Reference{ + Type: &pbresource.Type{ + Group: "test", + GroupVersion: "v1", + Kind: "fake", + }, + Tenancy: &pbresource.Tenancy{ + Partition: "test", + }, + } + + require.Equal(t, []byte("test\x00fake\x00test\x00"), PrefixIndexFromRefOrID(ref)) + }) + + t.Run("name prefix", func(t *testing.T) { + id := &pbresource.ID{ + Type: &pbresource.Type{ + Group: "test", + GroupVersion: "v1", + Kind: "fake", + }, + Tenancy: &pbresource.Tenancy{ + Partition: "test", + Namespace: "test", + }, + Name: "prefix", + } + + require.Equal(t, []byte("test\x00fake\x00test\x00test\x00prefix"), PrefixIndexFromRefOrID(id)) + }) +} + +func TestPrefixIndexFromTenancy(t *testing.T) { + t.Run("nil tenancy", func(t *testing.T) { + idx, done := prefixIndexFromTenancy(nil) + require.Len(t, idx, 0) + require.True(t, done) + }) + + t.Run("partition empty", func(t *testing.T) { + tenant := &pbresource.Tenancy{} + idx, done := prefixIndexFromTenancy(tenant) + require.Len(t, idx, 0) + require.True(t, done) + }) + + t.Run("partition wildcard", func(t *testing.T) { + tenant := &pbresource.Tenancy{ + Partition: storage.Wildcard, + } + idx, done := prefixIndexFromTenancy(tenant) + require.Len(t, idx, 0) + require.True(t, done) + }) + + t.Run("namespace empty", func(t *testing.T) { + tenant := &pbresource.Tenancy{ + Partition: "test", + Namespace: "", + } + idx, done := prefixIndexFromTenancy(tenant) + require.Equal(t, []byte("test\x00"), idx) + require.True(t, done) + }) + + t.Run("namespace wildcard", func(t *testing.T) { + tenant := &pbresource.Tenancy{ + Partition: "test", + Namespace: storage.Wildcard, + } + idx, done := prefixIndexFromTenancy(tenant) + require.Equal(t, []byte("test\x00"), idx) + require.True(t, done) + }) + + t.Run("namespace populated", func(t *testing.T) { + tenant := &pbresource.Tenancy{ + Partition: "test", + Namespace: "blah", + } + idx, done := prefixIndexFromTenancy(tenant) + require.Equal(t, []byte("test\x00blah\x00"), idx) + require.False(t, done) + }) +} + +func TestIndexFromType(t *testing.T) { + val := IndexFromType(&pbresource.Type{ + Group: "test", + GroupVersion: "v1", + Kind: "fake", + }) + + require.Equal(t, []byte("test\x00fake\x00"), val) + + val = IndexFromType(&pbresource.Type{ + Group: "test", + GroupVersion: "v1", + }) + + require.Equal(t, []byte("test\x00\x00"), val) +} + +func TestReferenceOrIDFromArgs(t *testing.T) { + ref := &pbresource.Reference{ + Type: &pbresource.Type{ + Group: "test", + GroupVersion: "v1", + Kind: "fake", + }, + Tenancy: &pbresource.Tenancy{ + Partition: "default", + Namespace: "default", + }, + Name: "foo", + } + t.Run("invalid length", func(t *testing.T) { + // the second arg will cause a validation error + val, err := ReferenceOrIDFromArgs(ref, 2) + require.Nil(t, val) + require.Error(t, err) + }) + + t.Run("invalid type", func(t *testing.T) { + val, err := ReferenceOrIDFromArgs("string type unexpected") + require.Nil(t, val) + require.Error(t, err) + }) + + t.Run("ok", func(t *testing.T) { + val, err := ReferenceOrIDFromArgs(ref) + require.NoError(t, err) + require.Equal(t, IndexFromRefOrID(ref), val) + }) +} + +func TestPrefixReferenceOrIDFromArgs(t *testing.T) { + ref := &pbresource.Reference{ + Type: &pbresource.Type{ + Group: "test", + GroupVersion: "v1", + Kind: "fake", + }, + Tenancy: &pbresource.Tenancy{ + Partition: "default", + }, + } + t.Run("invalid length", func(t *testing.T) { + // the second arg will cause a validation error + val, err := PrefixReferenceOrIDFromArgs(ref, 2) + require.Nil(t, val) + require.Error(t, err) + }) + + t.Run("invalid type", func(t *testing.T) { + val, err := PrefixReferenceOrIDFromArgs("string type unexpected") + require.Nil(t, val) + require.Error(t, err) + }) + + t.Run("ok", func(t *testing.T) { + val, err := PrefixReferenceOrIDFromArgs(ref) + require.NoError(t, err) + require.Equal(t, PrefixIndexFromRefOrID(ref), val) + }) +} + +func TestSingleValueFromArgs(t *testing.T) { + injectedError := errors.New("injected test error") + + idx := SingleValueFromArgs(func(val string) ([]byte, error) { + return []byte(val), nil + }) + + errIdx := SingleValueFromArgs(func(val string) ([]byte, error) { + return nil, injectedError + }) + + t.Run("invalid length", func(t *testing.T) { + // the second arg will cause a validation error + val, err := idx("blah", 2) + require.Nil(t, val) + require.Error(t, err) + }) + + t.Run("invalid type", func(t *testing.T) { + val, err := idx(3) + require.Nil(t, val) + require.Error(t, err) + }) + + t.Run("propagate error", func(t *testing.T) { + val, err := errIdx("blah") + require.Nil(t, val) + require.ErrorIs(t, err, injectedError) + }) + + t.Run("ok", func(t *testing.T) { + val, err := idx("blah") + require.NoError(t, err) + require.Equal(t, []byte("blah"), val) + }) +} + +func TestSingleValueFromOneOrTwoArgs(t *testing.T) { + injectedError := errors.New("injected test error") + + idx := SingleValueFromOneOrTwoArgs(func(val string, optional bool) ([]byte, error) { + if optional { + return []byte(val + val), nil + } + return []byte(val), nil + }) + + errIdx := SingleValueFromOneOrTwoArgs(func(val string, optional bool) ([]byte, error) { + return nil, injectedError + }) + + t.Run("invalid length", func(t *testing.T) { + // the third arg will cause a validation error + val, err := idx("blah", true, 4) + require.Nil(t, val) + require.Error(t, err) + }) + + t.Run("invalid first type", func(t *testing.T) { + val, err := idx(4, true) + require.Nil(t, val) + require.Error(t, err) + }) + + t.Run("invalid second type", func(t *testing.T) { + val, err := idx("blah", 3) + require.Nil(t, val) + require.Error(t, err) + }) + + t.Run("propagate error", func(t *testing.T) { + val, err := errIdx("blah") + require.Nil(t, val) + require.ErrorIs(t, err, injectedError) + }) + + t.Run("one arg ok", func(t *testing.T) { + val, err := idx("blah") + require.NoError(t, err) + require.Equal(t, []byte("blah"), val) + }) + + t.Run("two arg ok", func(t *testing.T) { + val, err := idx("blah", true) + require.NoError(t, err) + require.Equal(t, []byte("blahblah"), val) + }) + +} diff --git a/internal/controller/cache/index/errors.go b/internal/controller/cache/index/errors.go new file mode 100644 index 0000000000..4977e88d13 --- /dev/null +++ b/internal/controller/cache/index/errors.go @@ -0,0 +1,16 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package index + +import ( + "fmt" +) + +type MissingRequiredIndexError struct { + Name string +} + +func (err MissingRequiredIndexError) Error() string { + return fmt.Sprintf("the indexer produced no value for the required %q index", err.Name) +} diff --git a/internal/controller/cache/index/errors_test.go b/internal/controller/cache/index/errors_test.go new file mode 100644 index 0000000000..d3d96c65f8 --- /dev/null +++ b/internal/controller/cache/index/errors_test.go @@ -0,0 +1,16 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package index + +import ( + "testing" + + "github.com/hashicorp/consul/internal/testing/errors" +) + +func TestErrorStrings(t *testing.T) { + errors.TestErrorStrings(t, map[string]error{ + "MissingRequiredIndex": MissingRequiredIndexError{Name: "fake-index-name"}, + }) +} diff --git a/internal/controller/cache/index/index.go b/internal/controller/cache/index/index.go new file mode 100644 index 0000000000..66f39ad024 --- /dev/null +++ b/internal/controller/cache/index/index.go @@ -0,0 +1,84 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package index + +import ( + "github.com/hashicorp/consul/proto-public/pbresource" + iradix "github.com/hashicorp/go-immutable-radix/v2" +) + +type Index struct { + name string + required bool + indexer MultiIndexer + tree *iradix.Tree[[]*pbresource.Resource] +} + +func New(name string, i Indexer, opts ...IndexOption) *Index { + if name == "" { + panic("all indexers must have a non-empty name") + } + if i == nil { + panic("no indexer was supplied when creating a new cache Index") + } + + var multiIndexer MultiIndexer + switch v := i.(type) { + case SingleIndexer: + multiIndexer = singleIndexWrapper{indexer: v} + case MultiIndexer: + multiIndexer = v + default: + panic("The Indexer must also implement one of the SingleIndexer or MultiIndexer interfaces") + } + + idx := &Index{ + name: name, + indexer: multiIndexer, + tree: iradix.New[[]*pbresource.Resource](), + } + + for _, opt := range opts { + opt(idx) + } + + return idx +} + +func (i *Index) Name() string { + return i.name +} + +func (i *Index) Txn() Txn { + return &txn{ + inner: i.tree.Txn(), + index: i, + dirty: false, + } +} + +func (i *Index) fromArgs(args ...any) ([]byte, error) { + return i.indexer.FromArgs(args...) +} + +func (i *Index) fromResource(r *pbresource.Resource) (bool, [][]byte, error) { + return i.indexer.FromResource(r) +} + +type singleIndexWrapper struct { + indexer SingleIndexer +} + +func (s singleIndexWrapper) FromArgs(args ...any) ([]byte, error) { + return s.indexer.FromArgs(args...) +} + +func (s singleIndexWrapper) FromResource(r *pbresource.Resource) (bool, [][]byte, error) { + indexed, val, err := s.indexer.FromResource(r) + if err != nil || !indexed { + return false, nil, err + } + + return true, [][]byte{val}, nil +} diff --git a/internal/controller/cache/index/index_test.go b/internal/controller/cache/index/index_test.go new file mode 100644 index 0000000000..1f65e6d3ab --- /dev/null +++ b/internal/controller/cache/index/index_test.go @@ -0,0 +1,137 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package index + +import ( + "errors" + "testing" + + "github.com/hashicorp/consul/internal/controller/cache/index/indexmock" + "github.com/hashicorp/consul/internal/resource/resourcetest" + "github.com/hashicorp/consul/proto-public/pbresource" + "github.com/stretchr/testify/require" +) + +type testSingleIndexer struct{} + +func (testSingleIndexer) FromArgs(args ...any) ([]byte, error) { + return ReferenceOrIDFromArgs(args) +} + +func (testSingleIndexer) FromResource(*pbresource.Resource) (bool, []byte, error) { + return false, nil, nil +} + +type testMultiIndexer struct{} + +func (testMultiIndexer) FromArgs(args ...any) ([]byte, error) { + return ReferenceOrIDFromArgs(args) +} + +func (testMultiIndexer) FromResource(*pbresource.Resource) (bool, [][]byte, error) { + return false, nil, nil +} + +type argsOnlyIdx struct{} + +func (argsOnlyIdx) FromArgs(args ...any) ([]byte, error) { + return nil, nil +} + +func TestNew(t *testing.T) { + t.Run("no name", func(t *testing.T) { + require.Panics(t, func() { + New("", testSingleIndexer{}) + }) + }) + + t.Run("nil indexer", func(t *testing.T) { + require.Panics(t, func() { + New("test", nil) + }) + }) + + t.Run("indexer interface not satisfied", func(t *testing.T) { + require.Panics(t, func() { + New("test", argsOnlyIdx{}) + }) + }) + + t.Run("single indexer", func(t *testing.T) { + require.NotNil(t, New("test", testSingleIndexer{})) + }) + + t.Run("multi indexer", func(t *testing.T) { + require.NotNil(t, New("test", testMultiIndexer{})) + }) + + t.Run("required", func(t *testing.T) { + idx := New("test", testSingleIndexer{}, IndexRequired) + require.NotNil(t, idx) + require.True(t, idx.required) + require.Equal(t, "test", idx.Name()) + }) +} + +func TestSingleIndexWrapper(t *testing.T) { + injectedError := errors.New("injected") + rtype := &pbresource.Type{ + Group: "test", + GroupVersion: "v1", + Kind: "fake", + } + res := resourcetest.Resource(rtype, "foo").Build() + + t.Run("FromArgs ok", func(t *testing.T) { + m := indexmock.NewSingleIndexer(t) + wrapper := singleIndexWrapper{indexer: m} + + m.On("FromArgs", 1).Return([]byte{1, 2, 3}, nil) + vals, err := wrapper.FromArgs(1) + require.NoError(t, err) + require.Equal(t, []byte{1, 2, 3}, vals) + }) + + t.Run("FromArgs err", func(t *testing.T) { + m := indexmock.NewSingleIndexer(t) + wrapper := singleIndexWrapper{indexer: m} + + m.On("FromArgs", 1).Return([]byte(nil), injectedError) + vals, err := wrapper.FromArgs(1) + require.Error(t, err) + require.ErrorIs(t, err, injectedError) + require.Nil(t, vals) + }) + + t.Run("FromResource err", func(t *testing.T) { + m := indexmock.NewSingleIndexer(t) + wrapper := singleIndexWrapper{indexer: m} + m.On("FromResource", res).Return(false, []byte(nil), injectedError) + indexed, vals, err := wrapper.FromResource(res) + require.False(t, indexed) + require.Nil(t, vals) + require.ErrorIs(t, err, injectedError) + }) + + t.Run("FromResource not indexed", func(t *testing.T) { + m := indexmock.NewSingleIndexer(t) + wrapper := singleIndexWrapper{indexer: m} + m.On("FromResource", res).Return(false, []byte(nil), nil) + indexed, vals, err := wrapper.FromResource(res) + require.False(t, indexed) + require.Nil(t, vals) + require.Nil(t, err) + }) + + t.Run("FromResource ok", func(t *testing.T) { + m := indexmock.NewSingleIndexer(t) + wrapper := singleIndexWrapper{indexer: m} + m.On("FromResource", res).Return(true, []byte{1, 2, 3}, nil) + indexed, vals, err := wrapper.FromResource(res) + require.NoError(t, err) + require.True(t, indexed) + require.Len(t, vals, 1) + require.Equal(t, []byte{1, 2, 3}, vals[0]) + }) +} diff --git a/internal/controller/cache/index/indexmock/mock_Indexer.go b/internal/controller/cache/index/indexmock/mock_Indexer.go new file mode 100644 index 0000000000..28426ab5bb --- /dev/null +++ b/internal/controller/cache/index/indexmock/mock_Indexer.go @@ -0,0 +1,95 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package indexmock + +import mock "github.com/stretchr/testify/mock" + +// Indexer is an autogenerated mock type for the Indexer type +type Indexer struct { + mock.Mock +} + +type Indexer_Expecter struct { + mock *mock.Mock +} + +func (_m *Indexer) EXPECT() *Indexer_Expecter { + return &Indexer_Expecter{mock: &_m.Mock} +} + +// FromArgs provides a mock function with given fields: args +func (_m *Indexer) FromArgs(args ...interface{}) ([]byte, error) { + var _ca []interface{} + _ca = append(_ca, args...) + ret := _m.Called(_ca...) + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(...interface{}) ([]byte, error)); ok { + return rf(args...) + } + if rf, ok := ret.Get(0).(func(...interface{}) []byte); ok { + r0 = rf(args...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(...interface{}) error); ok { + r1 = rf(args...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Indexer_FromArgs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FromArgs' +type Indexer_FromArgs_Call struct { + *mock.Call +} + +// FromArgs is a helper method to define mock.On call +// - args ...interface{} +func (_e *Indexer_Expecter) FromArgs(args ...interface{}) *Indexer_FromArgs_Call { + return &Indexer_FromArgs_Call{Call: _e.mock.On("FromArgs", + append([]interface{}{}, args...)...)} +} + +func (_c *Indexer_FromArgs_Call) Run(run func(args ...interface{})) *Indexer_FromArgs_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]interface{}, len(args)-0) + for i, a := range args[0:] { + if a != nil { + variadicArgs[i] = a.(interface{}) + } + } + run(variadicArgs...) + }) + return _c +} + +func (_c *Indexer_FromArgs_Call) Return(_a0 []byte, _a1 error) *Indexer_FromArgs_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Indexer_FromArgs_Call) RunAndReturn(run func(...interface{}) ([]byte, error)) *Indexer_FromArgs_Call { + _c.Call.Return(run) + return _c +} + +// NewIndexer creates a new instance of Indexer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewIndexer(t interface { + mock.TestingT + Cleanup(func()) +}) *Indexer { + mock := &Indexer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/controller/cache/index/indexmock/mock_MultiIndexer.go b/internal/controller/cache/index/indexmock/mock_MultiIndexer.go new file mode 100644 index 0000000000..24f24313cc --- /dev/null +++ b/internal/controller/cache/index/indexmock/mock_MultiIndexer.go @@ -0,0 +1,159 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package indexmock + +import ( + pbresource "github.com/hashicorp/consul/proto-public/pbresource" + mock "github.com/stretchr/testify/mock" +) + +// MultiIndexer is an autogenerated mock type for the MultiIndexer type +type MultiIndexer struct { + mock.Mock +} + +type MultiIndexer_Expecter struct { + mock *mock.Mock +} + +func (_m *MultiIndexer) EXPECT() *MultiIndexer_Expecter { + return &MultiIndexer_Expecter{mock: &_m.Mock} +} + +// FromArgs provides a mock function with given fields: args +func (_m *MultiIndexer) FromArgs(args ...interface{}) ([]byte, error) { + var _ca []interface{} + _ca = append(_ca, args...) + ret := _m.Called(_ca...) + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(...interface{}) ([]byte, error)); ok { + return rf(args...) + } + if rf, ok := ret.Get(0).(func(...interface{}) []byte); ok { + r0 = rf(args...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(...interface{}) error); ok { + r1 = rf(args...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MultiIndexer_FromArgs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FromArgs' +type MultiIndexer_FromArgs_Call struct { + *mock.Call +} + +// FromArgs is a helper method to define mock.On call +// - args ...interface{} +func (_e *MultiIndexer_Expecter) FromArgs(args ...interface{}) *MultiIndexer_FromArgs_Call { + return &MultiIndexer_FromArgs_Call{Call: _e.mock.On("FromArgs", + append([]interface{}{}, args...)...)} +} + +func (_c *MultiIndexer_FromArgs_Call) Run(run func(args ...interface{})) *MultiIndexer_FromArgs_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]interface{}, len(args)-0) + for i, a := range args[0:] { + if a != nil { + variadicArgs[i] = a.(interface{}) + } + } + run(variadicArgs...) + }) + return _c +} + +func (_c *MultiIndexer_FromArgs_Call) Return(_a0 []byte, _a1 error) *MultiIndexer_FromArgs_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MultiIndexer_FromArgs_Call) RunAndReturn(run func(...interface{}) ([]byte, error)) *MultiIndexer_FromArgs_Call { + _c.Call.Return(run) + return _c +} + +// FromResource provides a mock function with given fields: r +func (_m *MultiIndexer) FromResource(r *pbresource.Resource) (bool, [][]byte, error) { + ret := _m.Called(r) + + var r0 bool + var r1 [][]byte + var r2 error + if rf, ok := ret.Get(0).(func(*pbresource.Resource) (bool, [][]byte, error)); ok { + return rf(r) + } + if rf, ok := ret.Get(0).(func(*pbresource.Resource) bool); ok { + r0 = rf(r) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(*pbresource.Resource) [][]byte); ok { + r1 = rf(r) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).([][]byte) + } + } + + if rf, ok := ret.Get(2).(func(*pbresource.Resource) error); ok { + r2 = rf(r) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// MultiIndexer_FromResource_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FromResource' +type MultiIndexer_FromResource_Call struct { + *mock.Call +} + +// FromResource is a helper method to define mock.On call +// - r *pbresource.Resource +func (_e *MultiIndexer_Expecter) FromResource(r interface{}) *MultiIndexer_FromResource_Call { + return &MultiIndexer_FromResource_Call{Call: _e.mock.On("FromResource", r)} +} + +func (_c *MultiIndexer_FromResource_Call) Run(run func(r *pbresource.Resource)) *MultiIndexer_FromResource_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*pbresource.Resource)) + }) + return _c +} + +func (_c *MultiIndexer_FromResource_Call) Return(_a0 bool, _a1 [][]byte, _a2 error) *MultiIndexer_FromResource_Call { + _c.Call.Return(_a0, _a1, _a2) + return _c +} + +func (_c *MultiIndexer_FromResource_Call) RunAndReturn(run func(*pbresource.Resource) (bool, [][]byte, error)) *MultiIndexer_FromResource_Call { + _c.Call.Return(run) + return _c +} + +// NewMultiIndexer creates a new instance of MultiIndexer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMultiIndexer(t interface { + mock.TestingT + Cleanup(func()) +}) *MultiIndexer { + mock := &MultiIndexer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/controller/cache/index/indexmock/mock_ResourceIterator.go b/internal/controller/cache/index/indexmock/mock_ResourceIterator.go new file mode 100644 index 0000000000..c248a3e039 --- /dev/null +++ b/internal/controller/cache/index/indexmock/mock_ResourceIterator.go @@ -0,0 +1,78 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package indexmock + +import ( + pbresource "github.com/hashicorp/consul/proto-public/pbresource" + mock "github.com/stretchr/testify/mock" +) + +// ResourceIterator is an autogenerated mock type for the ResourceIterator type +type ResourceIterator struct { + mock.Mock +} + +type ResourceIterator_Expecter struct { + mock *mock.Mock +} + +func (_m *ResourceIterator) EXPECT() *ResourceIterator_Expecter { + return &ResourceIterator_Expecter{mock: &_m.Mock} +} + +// Next provides a mock function with given fields: +func (_m *ResourceIterator) Next() *pbresource.Resource { + ret := _m.Called() + + var r0 *pbresource.Resource + if rf, ok := ret.Get(0).(func() *pbresource.Resource); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbresource.Resource) + } + } + + return r0 +} + +// ResourceIterator_Next_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Next' +type ResourceIterator_Next_Call struct { + *mock.Call +} + +// Next is a helper method to define mock.On call +func (_e *ResourceIterator_Expecter) Next() *ResourceIterator_Next_Call { + return &ResourceIterator_Next_Call{Call: _e.mock.On("Next")} +} + +func (_c *ResourceIterator_Next_Call) Run(run func()) *ResourceIterator_Next_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ResourceIterator_Next_Call) Return(_a0 *pbresource.Resource) *ResourceIterator_Next_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ResourceIterator_Next_Call) RunAndReturn(run func() *pbresource.Resource) *ResourceIterator_Next_Call { + _c.Call.Return(run) + return _c +} + +// NewResourceIterator creates a new instance of ResourceIterator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewResourceIterator(t interface { + mock.TestingT + Cleanup(func()) +}) *ResourceIterator { + mock := &ResourceIterator{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/controller/cache/index/indexmock/mock_SingleIndexer.go b/internal/controller/cache/index/indexmock/mock_SingleIndexer.go new file mode 100644 index 0000000000..cba9a6d2e6 --- /dev/null +++ b/internal/controller/cache/index/indexmock/mock_SingleIndexer.go @@ -0,0 +1,159 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package indexmock + +import ( + pbresource "github.com/hashicorp/consul/proto-public/pbresource" + mock "github.com/stretchr/testify/mock" +) + +// SingleIndexer is an autogenerated mock type for the SingleIndexer type +type SingleIndexer struct { + mock.Mock +} + +type SingleIndexer_Expecter struct { + mock *mock.Mock +} + +func (_m *SingleIndexer) EXPECT() *SingleIndexer_Expecter { + return &SingleIndexer_Expecter{mock: &_m.Mock} +} + +// FromArgs provides a mock function with given fields: args +func (_m *SingleIndexer) FromArgs(args ...interface{}) ([]byte, error) { + var _ca []interface{} + _ca = append(_ca, args...) + ret := _m.Called(_ca...) + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(...interface{}) ([]byte, error)); ok { + return rf(args...) + } + if rf, ok := ret.Get(0).(func(...interface{}) []byte); ok { + r0 = rf(args...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(...interface{}) error); ok { + r1 = rf(args...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SingleIndexer_FromArgs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FromArgs' +type SingleIndexer_FromArgs_Call struct { + *mock.Call +} + +// FromArgs is a helper method to define mock.On call +// - args ...interface{} +func (_e *SingleIndexer_Expecter) FromArgs(args ...interface{}) *SingleIndexer_FromArgs_Call { + return &SingleIndexer_FromArgs_Call{Call: _e.mock.On("FromArgs", + append([]interface{}{}, args...)...)} +} + +func (_c *SingleIndexer_FromArgs_Call) Run(run func(args ...interface{})) *SingleIndexer_FromArgs_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]interface{}, len(args)-0) + for i, a := range args[0:] { + if a != nil { + variadicArgs[i] = a.(interface{}) + } + } + run(variadicArgs...) + }) + return _c +} + +func (_c *SingleIndexer_FromArgs_Call) Return(_a0 []byte, _a1 error) *SingleIndexer_FromArgs_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *SingleIndexer_FromArgs_Call) RunAndReturn(run func(...interface{}) ([]byte, error)) *SingleIndexer_FromArgs_Call { + _c.Call.Return(run) + return _c +} + +// FromResource provides a mock function with given fields: r +func (_m *SingleIndexer) FromResource(r *pbresource.Resource) (bool, []byte, error) { + ret := _m.Called(r) + + var r0 bool + var r1 []byte + var r2 error + if rf, ok := ret.Get(0).(func(*pbresource.Resource) (bool, []byte, error)); ok { + return rf(r) + } + if rf, ok := ret.Get(0).(func(*pbresource.Resource) bool); ok { + r0 = rf(r) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(*pbresource.Resource) []byte); ok { + r1 = rf(r) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).([]byte) + } + } + + if rf, ok := ret.Get(2).(func(*pbresource.Resource) error); ok { + r2 = rf(r) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// SingleIndexer_FromResource_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FromResource' +type SingleIndexer_FromResource_Call struct { + *mock.Call +} + +// FromResource is a helper method to define mock.On call +// - r *pbresource.Resource +func (_e *SingleIndexer_Expecter) FromResource(r interface{}) *SingleIndexer_FromResource_Call { + return &SingleIndexer_FromResource_Call{Call: _e.mock.On("FromResource", r)} +} + +func (_c *SingleIndexer_FromResource_Call) Run(run func(r *pbresource.Resource)) *SingleIndexer_FromResource_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*pbresource.Resource)) + }) + return _c +} + +func (_c *SingleIndexer_FromResource_Call) Return(_a0 bool, _a1 []byte, _a2 error) *SingleIndexer_FromResource_Call { + _c.Call.Return(_a0, _a1, _a2) + return _c +} + +func (_c *SingleIndexer_FromResource_Call) RunAndReturn(run func(*pbresource.Resource) (bool, []byte, error)) *SingleIndexer_FromResource_Call { + _c.Call.Return(run) + return _c +} + +// NewSingleIndexer creates a new instance of SingleIndexer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewSingleIndexer(t interface { + mock.TestingT + Cleanup(func()) +}) *SingleIndexer { + mock := &SingleIndexer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/controller/cache/index/indexmock/mock_resourceIterable.go b/internal/controller/cache/index/indexmock/mock_resourceIterable.go new file mode 100644 index 0000000000..8c5646f19b --- /dev/null +++ b/internal/controller/cache/index/indexmock/mock_resourceIterable.go @@ -0,0 +1,97 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package indexmock + +import ( + pbresource "github.com/hashicorp/consul/proto-public/pbresource" + mock "github.com/stretchr/testify/mock" +) + +// ResourceIterable is an autogenerated mock type for the resourceIterable type +type ResourceIterable struct { + mock.Mock +} + +type ResourceIterable_Expecter struct { + mock *mock.Mock +} + +func (_m *ResourceIterable) EXPECT() *ResourceIterable_Expecter { + return &ResourceIterable_Expecter{mock: &_m.Mock} +} + +// Next provides a mock function with given fields: +func (_m *ResourceIterable) Next() ([]byte, []*pbresource.Resource, bool) { + ret := _m.Called() + + var r0 []byte + var r1 []*pbresource.Resource + var r2 bool + if rf, ok := ret.Get(0).(func() ([]byte, []*pbresource.Resource, bool)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() []byte); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func() []*pbresource.Resource); ok { + r1 = rf() + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).([]*pbresource.Resource) + } + } + + if rf, ok := ret.Get(2).(func() bool); ok { + r2 = rf() + } else { + r2 = ret.Get(2).(bool) + } + + return r0, r1, r2 +} + +// ResourceIterable_Next_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Next' +type ResourceIterable_Next_Call struct { + *mock.Call +} + +// Next is a helper method to define mock.On call +func (_e *ResourceIterable_Expecter) Next() *ResourceIterable_Next_Call { + return &ResourceIterable_Next_Call{Call: _e.mock.On("Next")} +} + +func (_c *ResourceIterable_Next_Call) Run(run func()) *ResourceIterable_Next_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ResourceIterable_Next_Call) Return(_a0 []byte, _a1 []*pbresource.Resource, _a2 bool) *ResourceIterable_Next_Call { + _c.Call.Return(_a0, _a1, _a2) + return _c +} + +func (_c *ResourceIterable_Next_Call) RunAndReturn(run func() ([]byte, []*pbresource.Resource, bool)) *ResourceIterable_Next_Call { + _c.Call.Return(run) + return _c +} + +// NewResourceIterable creates a new instance of ResourceIterable. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewResourceIterable(t interface { + mock.TestingT + Cleanup(func()) +}) *ResourceIterable { + mock := &ResourceIterable{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/controller/cache/index/interfaces.go b/internal/controller/cache/index/interfaces.go new file mode 100644 index 0000000000..e890c3d269 --- /dev/null +++ b/internal/controller/cache/index/interfaces.go @@ -0,0 +1,60 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package index + +import "github.com/hashicorp/consul/proto-public/pbresource" + +// Indexer is the base interface that all indexers must implement. Additionally +// an indexer must also implement either the SingleIndexer or MultiIndexer interface +// +//go:generate mockery --name Indexer --with-expecter +type Indexer interface { + FromArgs(args ...interface{}) ([]byte, error) +} + +// SingleIndexer is the interface to use to extract a single index value from a +// resource. E.g. extracting the resources owner ID. +// +//go:generate mockery --name SingleIndexer --with-expecter +type SingleIndexer interface { + Indexer + FromResource(r *pbresource.Resource) (bool, []byte, error) +} + +// MultiIndexer is the interface to implement for extracting multiple index +// values from a resource. E.g. extracting all resource References from the +// slice of references. +// +//go:generate mockery --name MultiIndexer --with-expecter +type MultiIndexer interface { + Indexer + FromResource(r *pbresource.Resource) (bool, [][]byte, error) +} + +// IndexOption is a functional option to use to modify an Indexes behavior. +type IndexOption func(*Index) + +// IndexRequired will cause the index to return a MissingRequiredIndexError +// in the event that the indexer returns no values for a resource. +func IndexRequired(s *Index) { + s.required = true +} + +// ResourceIterator is the interface that will be returned to iterate through +// a collection of results. +// +//go:generate mockery --name ResourceIterator --with-expecter +type ResourceIterator interface { + Next() *pbresource.Resource +} + +// Txn is an interface to control changes to an index within an transaction. +type Txn interface { + Get(args ...any) (*pbresource.Resource, error) + ListIterator(args ...any) (ResourceIterator, error) + ParentsIterator(args ...any) (ResourceIterator, error) + Insert(r *pbresource.Resource) error + Delete(r *pbresource.Resource) error + Commit() +} diff --git a/internal/controller/cache/index/iterator.go b/internal/controller/cache/index/iterator.go new file mode 100644 index 0000000000..9061623673 --- /dev/null +++ b/internal/controller/cache/index/iterator.go @@ -0,0 +1,37 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package index + +import "github.com/hashicorp/consul/proto-public/pbresource" + +//go:generate mockery --name resourceIterable --with-expecter --exported +type resourceIterable interface { + Next() ([]byte, []*pbresource.Resource, bool) +} + +type resourceIterator struct { + current []*pbresource.Resource + iter resourceIterable +} + +func (i *resourceIterator) Next() *pbresource.Resource { + // Maybe get a new list from the internal iterator + if len(i.current) == 0 { + _, i.current, _ = i.iter.Next() + } + + var rsc *pbresource.Resource + switch len(i.current) { + case 0: + // we are completely out of data so we can return + case 1: + rsc = i.current[0] + i.current = nil + default: + rsc = i.current[0] + i.current = i.current[1:] + } + + return rsc +} diff --git a/internal/controller/cache/index/iterator_test.go b/internal/controller/cache/index/iterator_test.go new file mode 100644 index 0000000000..7b2f56749a --- /dev/null +++ b/internal/controller/cache/index/iterator_test.go @@ -0,0 +1,65 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package index + +import ( + "testing" + + "github.com/hashicorp/consul/internal/controller/cache/index/indexmock" + "github.com/hashicorp/consul/internal/resource" + "github.com/hashicorp/consul/internal/resource/resourcetest" + "github.com/hashicorp/consul/proto-public/pbresource" + "github.com/hashicorp/consul/proto/private/prototest" + "github.com/stretchr/testify/require" +) + +var ( + testResourceType = &pbresource.Type{ + Group: "test", + GroupVersion: "v1", + Kind: "fake", + } +) + +func testResource(name string) *pbresource.Resource { + return resourcetest.Resource(testResourceType, name). + WithTenancy(resource.DefaultNamespacedTenancy()). + Build() +} + +func TestResourceIteratorNext(t *testing.T) { + m := indexmock.NewResourceIterable(t) + + r1 := testResource("one") + + r2 := testResource("two") + r3 := testResource("three") + + m.EXPECT().Next().Once().Return(nil, []*pbresource.Resource{r1}, true) + m.EXPECT().Next().Once().Return(nil, []*pbresource.Resource{r2, r3}, false) + m.EXPECT().Next().Return(nil, nil, false) + + i := resourceIterator{ + iter: m, + } + + // iterator is processing the first list of items + actual := i.Next() + require.NotNil(t, actual) + prototest.AssertDeepEqual(t, r1, actual) + // iterator should now be processing the second list of items. + actual = i.Next() + require.NotNil(t, actual) + prototest.AssertDeepEqual(t, r2, actual) + // second element of second list returned + actual = i.Next() + require.NotNil(t, actual) + prototest.AssertDeepEqual(t, r3, actual) + // no more items so a call to Next should return nil + actual = i.Next() + require.Nil(t, actual) + // verify that it continues to return nil indefinitely without causing a panic. + actual = i.Next() + require.Nil(t, actual) +} diff --git a/internal/controller/cache/index/testdata/MissingRequiredIndex.golden b/internal/controller/cache/index/testdata/MissingRequiredIndex.golden new file mode 100644 index 0000000000..5095d1bb63 --- /dev/null +++ b/internal/controller/cache/index/testdata/MissingRequiredIndex.golden @@ -0,0 +1 @@ +the indexer produced no value for the required "fake-index-name" index \ No newline at end of file diff --git a/internal/controller/cache/index/txn.go b/internal/controller/cache/index/txn.go new file mode 100644 index 0000000000..6952c3cb63 --- /dev/null +++ b/internal/controller/cache/index/txn.go @@ -0,0 +1,173 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package index + +import ( + iradix "github.com/hashicorp/go-immutable-radix/v2" + + "github.com/hashicorp/consul/internal/resource" + "github.com/hashicorp/consul/proto-public/pbresource" +) + +type txn struct { + inner *iradix.Txn[[]*pbresource.Resource] + index *Index + dirty bool +} + +func (t *txn) Get(args ...any) (*pbresource.Resource, error) { + val, err := t.index.fromArgs(args...) + if err != nil { + return nil, err + } + + return t.getRaw(val), nil +} + +func (t *txn) getRaw(val []byte) *pbresource.Resource { + resources, found := t.inner.Get(val) + if !found || len(resources) < 1 { + return nil + } + + return resources[0] +} + +func (t *txn) ListIterator(args ...any) (ResourceIterator, error) { + val, err := t.index.fromArgs(args...) + if err != nil { + return nil, err + } + + iter := t.inner.Root().Iterator() + iter.SeekPrefix(val) + + return &resourceIterator{iter: iter}, nil +} + +func (t *txn) ParentsIterator(args ...any) (ResourceIterator, error) { + val, err := t.index.fromArgs(args...) + if err != nil { + return nil, err + } + + iter := t.inner.Root().PathIterator(val) + + return &resourceIterator{iter: iter}, nil +} + +func (t *txn) Insert(r *pbresource.Resource) error { + indexed, vals, err := t.index.fromResource(r) + if err != nil { + return err + } + + if !indexed && t.index.required { + return MissingRequiredIndexError{Name: t.index.Name()} + } + + for _, val := range vals { + if t.insertOne(val, r) { + t.dirty = true + } + } + + return nil +} + +func (t *txn) insertOne(idxVal []byte, r *pbresource.Resource) bool { + var newResources []*pbresource.Resource + existing, found := t.inner.Get(idxVal) + if found { + newResources = make([]*pbresource.Resource, 0, len(existing)+1) + found := false + for _, rsc := range existing { + if !resource.EqualID(rsc.GetId(), r.GetId()) { + newResources = append(newResources, rsc) + } else { + found = true + newResources = append(newResources, r) + } + } + + if !found { + newResources = append(newResources, r) + } + } else { + newResources = []*pbresource.Resource{r} + } + t.inner.Insert(idxVal, newResources) + return true +} + +func (t *txn) Delete(r *pbresource.Resource) error { + indexed, vals, err := t.index.fromResource(r) + if err != nil { + return err + } + + if !indexed && t.index.required { + return MissingRequiredIndexError{Name: t.index.Name()} + } + + for _, val := range vals { + if t.deleteOne(val, r) { + t.dirty = true + } + } + + return nil +} + +func (t *txn) deleteOne(idxVal []byte, r *pbresource.Resource) bool { + existing, found := t.inner.Get(idxVal) + if !found { + // this resource is not currently indexed + return false + } + + existingIdx := -1 + for idx, rsc := range existing { + if resource.EqualID(rsc.GetId(), r.GetId()) { + existingIdx = idx + break + } + } + + switch { + // The index value maps to some resources but none had the same id as the one we wish + // to delete. Therefore we can leave the slice alone because the delete is a no-op + case existingIdx < 0: + return false + + // We found something (existingIdex >= 0) but there is only one thing in the array. In + // this case we should remove the whole resource slice from the tree. + case len(existing) == 1: + t.inner.Delete(idxVal) + + // The first slice element is the resource to delete so we can reslice safely + case existingIdx == 0: + t.inner.Insert(idxVal, existing[1:]) + + // The last slice element is the resource to delete so we can reslice safely + case existingIdx == len(existing)-1: + t.inner.Insert(idxVal, existing[:len(existing)-1]) + + // The resource to delete exists somewhere in the middle of the slice. So we must + // recreate a new backing array to maintain immutability of the data being stored + default: + newResources := make([]*pbresource.Resource, len(existing)-1) + copy(newResources[0:existingIdx], existing[0:existingIdx]) + copy(newResources[existingIdx:], existing[existingIdx+1:]) + t.inner.Insert(idxVal, newResources) + } + + return true +} + +func (t *txn) Commit() { + if t.dirty { + t.index.tree = t.inner.Commit() + } +} diff --git a/internal/controller/cache/index/txn_test.go b/internal/controller/cache/index/txn_test.go new file mode 100644 index 0000000000..ee8815c4d1 --- /dev/null +++ b/internal/controller/cache/index/txn_test.go @@ -0,0 +1,382 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package index + +import ( + "errors" + "testing" + + "github.com/hashicorp/consul/internal/controller/cache/index/indexmock" + "github.com/hashicorp/consul/proto-public/pbresource" + "github.com/hashicorp/consul/proto/private/prototest" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" +) + +var fakeTestError = errors.New("fake test error") + +func TestTxn(t *testing.T) { + suite.Run(t, new(txnSuite)) +} + +type txnSuite struct { + suite.Suite + + indexer *indexmock.SingleIndexer + index *Index + + r1 *pbresource.Resource + r2 *pbresource.Resource + r11 *pbresource.Resource + r123 *pbresource.Resource +} + +func (suite *txnSuite) SetupTest() { + suite.indexer = indexmock.NewSingleIndexer(suite.T()) + suite.index = New("test", suite.indexer, IndexRequired) + + suite.r1 = testResource("r1") + suite.r2 = testResource("r2") + suite.r11 = testResource("r11") + suite.r123 = testResource("r123") + + exp := suite.indexer.EXPECT() + exp.FromResource(suite.r1).Return(true, PrefixIndexFromRefOrID(suite.r1.Id), nil).Once() + exp.FromResource(suite.r2).Return(true, PrefixIndexFromRefOrID(suite.r2.Id), nil).Once() + exp.FromResource(suite.r11).Return(true, PrefixIndexFromRefOrID(suite.r11.Id), nil).Once() + exp.FromResource(suite.r123).Return(true, PrefixIndexFromRefOrID(suite.r123.Id), nil).Once() + + txn := suite.index.Txn() + txn.Insert(suite.r1) + txn.Insert(suite.r2) + txn.Insert(suite.r11) + txn.Insert(suite.r123) + txn.Commit() +} + +func (suite *txnSuite) TestGet() { + suite.indexer.EXPECT(). + FromArgs(suite.r1.Id). + RunAndReturn(PrefixReferenceOrIDFromArgs). + Once() + + actual, err := suite.index.Txn().Get(suite.r1.Id) + require.NoError(suite.T(), err) + require.NotNil(suite.T(), actual) + prototest.AssertDeepEqual(suite.T(), suite.r1, actual) +} + +func (suite *txnSuite) TestGet_NotFound() { + suite.indexer.EXPECT(). + FromArgs(suite.r1.Id). + Return(nil, nil). + Once() + + actual, err := suite.index.Txn().Get(suite.r1.Id) + require.NoError(suite.T(), err) + require.Nil(suite.T(), actual) +} + +func (suite *txnSuite) TestGet_Error() { + suite.indexer.EXPECT(). + FromArgs(suite.r1.Id). + Return(nil, fakeTestError). + Once() + + actual, err := suite.index.Txn().Get(suite.r1.Id) + require.ErrorIs(suite.T(), err, fakeTestError) + require.Nil(suite.T(), actual) +} + +func (suite *txnSuite) TestListIterator() { + refQuery := &pbresource.Reference{ + Type: testResourceType, + } + + suite.indexer.EXPECT(). + FromArgs(refQuery). + // Calculate a prefix based query for use with the ListIterator + RunAndReturn(PrefixReferenceOrIDFromArgs). + Once() + + iter, err := suite.index.Txn().ListIterator(refQuery) + require.NoError(suite.T(), err) + + r := iter.Next() + require.NotNil(suite.T(), r) + prototest.AssertDeepEqual(suite.T(), suite.r1, r) + + r = iter.Next() + require.NotNil(suite.T(), r) + prototest.AssertDeepEqual(suite.T(), suite.r11, r) + + r = iter.Next() + require.NotNil(suite.T(), r) + prototest.AssertDeepEqual(suite.T(), suite.r123, r) + + r = iter.Next() + require.NotNil(suite.T(), r) + prototest.AssertDeepEqual(suite.T(), suite.r2, r) + + r = iter.Next() + require.Nil(suite.T(), r) +} + +func (suite *txnSuite) TestListIterator_Error() { + suite.indexer.EXPECT(). + // abusing the mock to create a shortened index for us. + FromArgs("sentinel"). + Return(nil, fakeTestError). + Once() + + iter, err := suite.index.Txn().ListIterator("sentinel") + require.ErrorIs(suite.T(), err, fakeTestError) + require.Nil(suite.T(), iter) +} + +func (suite *txnSuite) TestParentsIterator() { + suite.indexer.EXPECT(). + // abusing the mock to create a shortened index for us. + FromArgs(suite.r123.Id). + RunAndReturn(ReferenceOrIDFromArgs). + Once() + + iter, err := suite.index.Txn().ParentsIterator(suite.r123.Id) + require.NoError(suite.T(), err) + + r := iter.Next() + require.NotNil(suite.T(), r) + prototest.AssertDeepEqual(suite.T(), suite.r1, r) + + r = iter.Next() + require.NotNil(suite.T(), r) + prototest.AssertDeepEqual(suite.T(), suite.r123, r) + + r = iter.Next() + require.Nil(suite.T(), r) +} + +func (suite *txnSuite) TestParentsIterator_Error() { + suite.indexer.EXPECT(). + // abusing the mock to create a shortened index for us. + FromArgs("sentinel"). + Return(nil, fakeTestError). + Once() + + iter, err := suite.index.Txn().ParentsIterator("sentinel") + require.ErrorIs(suite.T(), err, fakeTestError) + require.Nil(suite.T(), iter) +} + +func (suite *txnSuite) TestInsert_MissingRequiredIndex() { + suite.indexer.EXPECT(). + FromResource(suite.r1). + Return(false, nil, nil). + Once() + + err := suite.index.Txn().Insert(suite.r1) + require.Error(suite.T(), err) + require.ErrorIs(suite.T(), err, MissingRequiredIndexError{Name: "test"}) +} + +func (suite *txnSuite) TestInsert_IndexError() { + suite.indexer.EXPECT(). + FromResource(suite.r1). + Return(false, nil, fakeTestError). + Once() + + err := suite.index.Txn().Insert(suite.r1) + require.Error(suite.T(), err) + require.ErrorIs(suite.T(), err, fakeTestError) +} + +func (suite *txnSuite) TestInsert_UpdateInternalSlice() { + // So if you look closely this is going to insert the newR + // resource but is calculating the index value of the r1 + // resource. This is done to exercise the insertion functionality + // where indexes are non-unique and at each leaf in the radix + // tree we must keep a list of items. + newR := testResource("newR") + suite.indexer.EXPECT(). + FromResource(newR). + Return(true, PrefixIndexFromRefOrID(suite.r1.Id), nil). + Once() + + // here we are setting up the expecation for re-inserting r1 + newR1 := testResource("r1") + suite.indexer.EXPECT(). + FromResource(newR1). + Return(true, PrefixIndexFromRefOrID(newR1.Id), nil). + Once() + + // Actually index the resource + txn := suite.index.Txn() + require.NoError(suite.T(), txn.Insert(newR)) + require.NoError(suite.T(), txn.Insert(newR1)) + txn.Commit() + + // No validate that the insertions worked correctly. + suite.indexer.EXPECT(). + FromArgs(newR1.Id). + RunAndReturn(PrefixReferenceOrIDFromArgs). + Once() + iter, err := suite.index.Txn().ListIterator(newR1.Id) + require.NoError(suite.T(), err) + + var resources []*pbresource.Resource + for r := iter.Next(); r != nil; r = iter.Next() { + resources = append(resources, r) + } + + prototest.AssertElementsMatch(suite.T(), []*pbresource.Resource{newR, newR1, suite.r11, suite.r123}, resources) +} + +func (suite *txnSuite) TestDelete() { + // expected to index the resource during deletion + suite.indexer.EXPECT(). + FromResource(suite.r1). + Return(true, PrefixIndexFromRefOrID(suite.r1.Id), nil). + Once() + + // perform the deletion + txn := suite.index.Txn() + require.NoError(suite.T(), txn.Delete(suite.r1)) + txn.Commit() + + // expect to index the ID during the query + suite.indexer.EXPECT(). + FromArgs(suite.r1.Id). + RunAndReturn(PrefixReferenceOrIDFromArgs). + Once() + + // ensure that the deletion worked + res, err := suite.index.Txn().Get(suite.r1.Id) + require.NoError(suite.T(), err) + require.Nil(suite.T(), res) +} + +func (suite *txnSuite) TestDelete_NotFound() { + res := testResource("foo") + suite.indexer.EXPECT(). + FromResource(res). + Return(true, PrefixIndexFromRefOrID(res.Id), nil). + Once() + + // attempt the deletion + txn := suite.index.Txn() + require.NoError(suite.T(), txn.Delete(res)) + txn.Commit() +} + +func (suite *txnSuite) TestDelete_IdxPresentValNotFound() { + // The index holds a radix tree that points to a slice of resources. + // A slice is used to account for non-unique indexes. This test case + // is meant to specifically exercise the case where the radix leaf + // node exists but a resource with an equivalent ID is not present + // in the slice. + + // Calculating the index from the r1 resource will ensure that a + // radix leaf exists but since newR was never inserted this should + // exercise the case where the resource is not found within the slice + newR := testResource("newR") + suite.indexer.EXPECT(). + FromResource(newR). + Return(true, PrefixIndexFromRefOrID(suite.r1.Id), nil). + Once() + + txn := suite.index.Txn() + require.NoError(suite.T(), txn.Delete(newR)) + txn.Commit() +} + +func (suite *txnSuite) TestDelete_SliceModifications() { + commonIndex := []byte("fake\x00") + + injectResource := func(name string) *pbresource.Resource { + r := testResource(name) + suite.indexer.EXPECT(). + FromResource(r). + Return(true, commonIndex, nil). + Once() + + txn := suite.index.Txn() + require.NoError(suite.T(), txn.Insert(r)) + txn.Commit() + + return r + } + + fr1 := injectResource("fr1") + fr2 := injectResource("fr2") + fr3 := injectResource("fr3") + fr4 := injectResource("fr4") + fr5 := injectResource("fr5") + + txn := suite.index.Txn() + + // excercise deletion of the first slice element + suite.indexer.EXPECT(). + FromResource(fr1). + Return(true, commonIndex, nil). + Once() + + require.NoError(suite.T(), txn.Delete(fr1)) + + // excercise deletion of the last slice element + suite.indexer.EXPECT(). + FromResource(fr5). + Return(true, commonIndex, nil). + Once() + + require.NoError(suite.T(), txn.Delete(fr5)) + + // excercise deletion from the middle of the list + suite.indexer.EXPECT(). + FromResource(fr3). + Return(true, commonIndex, nil). + Once() + + require.NoError(suite.T(), txn.Delete(fr3)) + + txn.Commit() + + // no verify that only fr2 and fr4 exist + suite.indexer.EXPECT(). + FromArgs(fr2.Id). + Return(commonIndex, nil). + Once() + + iter, err := suite.index.Txn().ListIterator(fr2.Id) + require.NoError(suite.T(), err) + require.NotNil(suite.T(), iter) + + var resources []*pbresource.Resource + for r := iter.Next(); r != nil; r = iter.Next() { + resources = append(resources, r) + } + + prototest.AssertElementsMatch(suite.T(), []*pbresource.Resource{fr2, fr4}, resources) +} + +func (suite *txnSuite) TestDelete_MissingRequiredIndex() { + suite.indexer.EXPECT(). + FromResource(suite.r1). + Return(false, nil, nil). + Once() + + err := suite.index.Txn().Delete(suite.r1) + require.Error(suite.T(), err) + require.ErrorIs(suite.T(), err, MissingRequiredIndexError{Name: "test"}) +} + +func (suite *txnSuite) TestDelete_IndexError() { + suite.indexer.EXPECT(). + FromResource(suite.r1). + Return(false, nil, fakeTestError). + Once() + + err := suite.index.Txn().Delete(suite.r1) + require.Error(suite.T(), err) + require.ErrorIs(suite.T(), err, fakeTestError) +} diff --git a/internal/controller/cache/indexers/.mockery.yaml b/internal/controller/cache/indexers/.mockery.yaml new file mode 100644 index 0000000000..5346da5866 --- /dev/null +++ b/internal/controller/cache/indexers/.mockery.yaml @@ -0,0 +1,15 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +with-expecter: true +all: true +recursive: false +# We don't want the mocks within proto-public so as to force a dependency +# of the testify library on the modules usage. The mocks are only for +# internal testing purposes. Other consumers can generated the mocks into +# their own code base. +dir: "indexersmock" +outpkg: "indexersmock" +mockname: "{{.InterfaceName}}" +packages: + github.com/hashicorp/consul/internal/controller/cache/indexers: diff --git a/internal/controller/cache/indexers/decoded_indexer.go b/internal/controller/cache/indexers/decoded_indexer.go new file mode 100644 index 0000000000..213f599387 --- /dev/null +++ b/internal/controller/cache/indexers/decoded_indexer.go @@ -0,0 +1,65 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package indexers + +import ( + "github.com/hashicorp/consul/internal/controller/cache/index" + "github.com/hashicorp/consul/internal/resource" + "github.com/hashicorp/consul/proto-public/pbresource" + "google.golang.org/protobuf/proto" +) + +type FromArgs func(args ...any) ([]byte, error) +type SingleIndexer[T proto.Message] func(r *resource.DecodedResource[T]) (bool, []byte, error) +type MultiIndexer[T proto.Message] func(r *resource.DecodedResource[T]) (bool, [][]byte, error) + +func DecodedSingleIndexer[T proto.Message](name string, args FromArgs, idx SingleIndexer[T]) *index.Index { + return index.New(name, &singleIndexer[T]{ + decodedIndexer: idx, + indexArgs: args, + }) +} + +func DecodedMultiIndexer[T proto.Message](name string, args FromArgs, idx MultiIndexer[T]) *index.Index { + return index.New(name, &multiIndexer[T]{ + indexArgs: args, + decodedIndexer: idx, + }) +} + +type singleIndexer[T proto.Message] struct { + indexArgs FromArgs + decodedIndexer SingleIndexer[T] +} + +func (i *singleIndexer[T]) FromArgs(args ...any) ([]byte, error) { + return i.indexArgs(args...) +} + +func (i *singleIndexer[T]) FromResource(r *pbresource.Resource) (bool, []byte, error) { + res, err := resource.Decode[T](r) + if err != nil { + return false, nil, err + } + + return i.decodedIndexer(res) +} + +type multiIndexer[T proto.Message] struct { + decodedIndexer MultiIndexer[T] + indexArgs FromArgs +} + +func (i *multiIndexer[T]) FromArgs(args ...any) ([]byte, error) { + return i.indexArgs(args...) +} + +func (i *multiIndexer[T]) FromResource(r *pbresource.Resource) (bool, [][]byte, error) { + res, err := resource.Decode[T](r) + if err != nil { + return false, nil, err + } + + return i.decodedIndexer(res) +} diff --git a/internal/controller/cache/indexers/decoded_indexer_test.go b/internal/controller/cache/indexers/decoded_indexer_test.go new file mode 100644 index 0000000000..c37f0d965b --- /dev/null +++ b/internal/controller/cache/indexers/decoded_indexer_test.go @@ -0,0 +1,295 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package indexers + +import ( + "errors" + "testing" + + "github.com/hashicorp/consul/internal/controller/cache/indexers/indexersmock" + "github.com/hashicorp/consul/internal/resource" + "github.com/hashicorp/consul/internal/resource/resourcetest" + pbdemo "github.com/hashicorp/consul/proto/private/pbdemo/v1" + "github.com/hashicorp/consul/proto/private/prototest" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" +) + +var ( + fakeTestError = errors.New("fake test error") +) + +func TestSingleIndexer(t *testing.T) { + suite.Run(t, &decodedSingleIndexerSuite{}) +} + +type decodedSingleIndexerSuite struct { + suite.Suite + + indexer *indexersmock.SingleIndexer[*pbdemo.Album] + args *indexersmock.FromArgs + + index *singleIndexer[*pbdemo.Album] +} + +func (suite *decodedSingleIndexerSuite) SetupTest() { + suite.indexer = indexersmock.NewSingleIndexer[*pbdemo.Album](suite.T()) + suite.args = indexersmock.NewFromArgs(suite.T()) + suite.index = &singleIndexer[*pbdemo.Album]{ + indexArgs: suite.args.Execute, + decodedIndexer: suite.indexer.Execute, + } +} + +func (suite *decodedSingleIndexerSuite) TestFromArgs() { + suite.args.EXPECT(). + Execute("blah", 1, true). + Return([]byte("foo"), nil). + Once() + + val, err := suite.index.FromArgs("blah", 1, true) + require.NoError(suite.T(), err) + require.Equal(suite.T(), []byte("foo"), val) +} + +func (suite *decodedSingleIndexerSuite) TestFromArgs_Error() { + suite.args.EXPECT(). + Execute("blah", 1, true). + Return(nil, fakeTestError). + Once() + + val, err := suite.index.FromArgs("blah", 1, true) + require.ErrorIs(suite.T(), err, fakeTestError) + require.Nil(suite.T(), val) +} + +func (suite *decodedSingleIndexerSuite) TestFromResource() { + res := resourcetest.Resource(pbdemo.AlbumType, "foo"). + WithData(suite.T(), &pbdemo.Album{ + Name: "blah", + }). + Build() + + dec := resourcetest.MustDecode[*pbdemo.Album](suite.T(), res) + + suite.indexer.EXPECT(). + Execute(dec). + Return(true, []byte{1, 2, 3}, nil). + Once() + + indexed, val, err := suite.index.FromResource(res) + require.True(suite.T(), indexed) + require.NoError(suite.T(), err) + require.Equal(suite.T(), []byte{1, 2, 3}, val) +} + +func (suite *decodedSingleIndexerSuite) TestFromResource_Error() { + res := resourcetest.Resource(pbdemo.AlbumType, "foo"). + WithData(suite.T(), &pbdemo.Album{ + Name: "blah", + }). + Build() + + dec := resourcetest.MustDecode[*pbdemo.Album](suite.T(), res) + + suite.indexer.EXPECT(). + Execute(dec). + Return(false, nil, fakeTestError). + Once() + + indexed, val, err := suite.index.FromResource(res) + require.False(suite.T(), indexed) + require.ErrorIs(suite.T(), err, fakeTestError) + require.Nil(suite.T(), val) +} + +func (suite *decodedSingleIndexerSuite) TestFromResource_DecodeError() { + res := resourcetest.Resource(pbdemo.ArtistType, "foo"). + WithData(suite.T(), &pbdemo.Artist{ + Name: "blah", + }). + Build() + + var expectedErr resource.ErrDataParse + indexed, val, err := suite.index.FromResource(res) + require.False(suite.T(), indexed) + require.ErrorAs(suite.T(), err, &expectedErr) + require.Nil(suite.T(), val) +} + +func (suite *decodedSingleIndexerSuite) TestIntegration() { + // This test attempts to do enough to ensure that the + // cache Index creator configures all the interfaces/funcs + // the correct way. It is not meant to fully test the + // Index type itself. + res := resourcetest.Resource(pbdemo.AlbumType, "foo"). + WithData(suite.T(), &pbdemo.Album{ + Name: "blah", + }). + Build() + + dec := resourcetest.MustDecode[*pbdemo.Album](suite.T(), res) + + idx := DecodedSingleIndexer("test", suite.args.Execute, suite.indexer.Execute) + + suite.indexer.EXPECT(). + Execute(dec). + Return(true, []byte{1, 2}, nil). + Once() + + txn := idx.Txn() + require.NoError(suite.T(), txn.Insert(res)) + txn.Commit() + + suite.args.EXPECT(). + Execute("fake"). + Return([]byte{1, 2}, nil). + Once() + + r, err := idx.Txn().Get("fake") + require.NoError(suite.T(), err) + prototest.AssertDeepEqual(suite.T(), res, r) +} + +func TestMultiIndexer(t *testing.T) { + suite.Run(t, &decodedMultiIndexerSuite{}) +} + +type decodedMultiIndexerSuite struct { + suite.Suite + + indexer *indexersmock.MultiIndexer[*pbdemo.Album] + args *indexersmock.FromArgs + + index *multiIndexer[*pbdemo.Album] +} + +func (suite *decodedMultiIndexerSuite) SetupTest() { + suite.indexer = indexersmock.NewMultiIndexer[*pbdemo.Album](suite.T()) + suite.args = indexersmock.NewFromArgs(suite.T()) + suite.index = &multiIndexer[*pbdemo.Album]{ + indexArgs: suite.args.Execute, + decodedIndexer: suite.indexer.Execute, + } +} + +func (suite *decodedMultiIndexerSuite) TestFromArgs() { + suite.args.EXPECT(). + Execute("blah", 1, true). + Return([]byte("foo"), nil). + Once() + + val, err := suite.index.FromArgs("blah", 1, true) + require.NoError(suite.T(), err) + require.Equal(suite.T(), []byte("foo"), val) +} + +func (suite *decodedMultiIndexerSuite) TestFromArgs_Error() { + suite.args.EXPECT(). + Execute("blah", 1, true). + Return(nil, fakeTestError). + Once() + + val, err := suite.index.FromArgs("blah", 1, true) + require.ErrorIs(suite.T(), err, fakeTestError) + require.Nil(suite.T(), val) +} + +func (suite *decodedMultiIndexerSuite) TestFromResource() { + res := resourcetest.Resource(pbdemo.AlbumType, "foo"). + WithData(suite.T(), &pbdemo.Album{ + Name: "blah", + }). + Build() + + dec := resourcetest.MustDecode[*pbdemo.Album](suite.T(), res) + + suite.indexer.EXPECT(). + Execute(dec). + Return(true, [][]byte{{1, 2}, {3}}, nil). + Once() + + indexed, val, err := suite.index.FromResource(res) + require.True(suite.T(), indexed) + require.NoError(suite.T(), err) + require.Equal(suite.T(), [][]byte{{1, 2}, {3}}, val) +} + +func (suite *decodedMultiIndexerSuite) TestFromResource_Error() { + res := resourcetest.Resource(pbdemo.AlbumType, "foo"). + WithData(suite.T(), &pbdemo.Album{ + Name: "blah", + }). + Build() + + dec := resourcetest.MustDecode[*pbdemo.Album](suite.T(), res) + + suite.indexer.EXPECT(). + Execute(dec). + Return(false, nil, fakeTestError). + Once() + + indexed, val, err := suite.index.FromResource(res) + require.False(suite.T(), indexed) + require.ErrorIs(suite.T(), err, fakeTestError) + require.Nil(suite.T(), val) +} + +func (suite *decodedMultiIndexerSuite) TestFromResource_DecodeError() { + res := resourcetest.Resource(pbdemo.ArtistType, "foo"). + WithData(suite.T(), &pbdemo.Artist{ + Name: "blah", + }). + Build() + + var expectedErr resource.ErrDataParse + indexed, val, err := suite.index.FromResource(res) + require.False(suite.T(), indexed) + require.ErrorAs(suite.T(), err, &expectedErr) + require.Nil(suite.T(), val) +} + +func (suite *decodedMultiIndexerSuite) TestIntegration() { + // This test attempts to do enough to ensure that the + // cache Index creator configures all the interfaces/funcs + // the correct way. It is not meant to fully test the + // Index type itself. + res := resourcetest.Resource(pbdemo.AlbumType, "foo"). + WithData(suite.T(), &pbdemo.Album{ + Name: "blah", + }). + Build() + + dec := resourcetest.MustDecode[*pbdemo.Album](suite.T(), res) + + idx := DecodedMultiIndexer("test", suite.args.Execute, suite.indexer.Execute) + + suite.indexer.EXPECT(). + Execute(dec). + Return(true, [][]byte{{1, 2}, {3}}, nil). + Once() + + txn := idx.Txn() + require.NoError(suite.T(), txn.Insert(res)) + txn.Commit() + + suite.args.EXPECT(). + Execute("fake"). + Return([]byte{1, 2}, nil). + Once() + + suite.args.EXPECT(). + Execute("fake2"). + Return([]byte{3}, nil). + Once() + + txn = idx.Txn() + r, err := txn.Get("fake") + require.NoError(suite.T(), err) + prototest.AssertDeepEqual(suite.T(), res, r) + + r, err = txn.Get("fake2") + require.NoError(suite.T(), err) + prototest.AssertDeepEqual(suite.T(), res, r) +} diff --git a/internal/controller/cache/indexers/id_indexer.go b/internal/controller/cache/indexers/id_indexer.go new file mode 100644 index 0000000000..f45358f40c --- /dev/null +++ b/internal/controller/cache/indexers/id_indexer.go @@ -0,0 +1,48 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package indexers + +import ( + "github.com/hashicorp/consul/internal/controller/cache/index" + "github.com/hashicorp/consul/internal/resource" + "github.com/hashicorp/consul/proto-public/pbresource" +) + +func IDIndex(name string, opts ...index.IndexOption) *index.Index { + return index.New(name, &idOrRefIndexer{getID: getResourceID}, opts...) +} + +func OwnerIndex(name string, opts ...index.IndexOption) *index.Index { + return index.New(name, &idOrRefIndexer{getID: getOwnerID}, opts...) +} + +func SingleIDOrRefIndex(name string, f GetSingleRefOrID, opts ...index.IndexOption) *index.Index { + return index.New(name, &idOrRefIndexer{getID: f}, opts...) +} + +//go:generate mockery --name GetSingleRefOrID --with-expecter +type GetSingleRefOrID func(*pbresource.Resource) resource.ReferenceOrID + +func getResourceID(r *pbresource.Resource) resource.ReferenceOrID { + return r.GetId() +} + +func getOwnerID(r *pbresource.Resource) resource.ReferenceOrID { + return r.GetOwner() +} + +type idOrRefIndexer struct { + getID GetSingleRefOrID +} + +// FromArgs constructs a radix tree key from an ID for lookup. +func (i idOrRefIndexer) FromArgs(args ...any) ([]byte, error) { + return index.ReferenceOrIDFromArgs(args...) +} + +// FromObject constructs a radix tree key from a Resource at write-time, or an +// ID at delete-time. +func (i idOrRefIndexer) FromResource(r *pbresource.Resource) (bool, []byte, error) { + return true, index.IndexFromRefOrID(i.getID(r)), nil +} diff --git a/internal/controller/cache/indexers/id_indexer_test.go b/internal/controller/cache/indexers/id_indexer_test.go new file mode 100644 index 0000000000..a70bd349d1 --- /dev/null +++ b/internal/controller/cache/indexers/id_indexer_test.go @@ -0,0 +1,96 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package indexers + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/hashicorp/consul/internal/controller/cache/index" + "github.com/hashicorp/consul/internal/controller/cache/indexers/indexersmock" + "github.com/hashicorp/consul/internal/resource/resourcetest" + "github.com/hashicorp/consul/proto-public/pbresource" + pbdemo "github.com/hashicorp/consul/proto/private/pbdemo/v1" + "github.com/hashicorp/consul/proto/private/prototest" +) + +func TestIDIndex(t *testing.T) { + idx := IDIndex("test", index.IndexRequired) + + r1 := resourcetest.Resource(pbdemo.AlbumType, "foo"). + WithTenancy(&pbresource.Tenancy{ + Partition: "default", + Namespace: "default", + }). + WithData(t, &pbdemo.Album{ + Name: "foo", + }). + Build() + + txn := idx.Txn() + require.NoError(t, txn.Insert(r1)) + txn.Commit() + + out, err := idx.Txn().Get(r1.Id) + require.NoError(t, err) + prototest.AssertDeepEqual(t, r1, out) +} + +func TestOwnerIndex(t *testing.T) { + idx := OwnerIndex("test", index.IndexRequired) + + r1 := resourcetest.Resource(pbdemo.AlbumType, "foo"). + WithTenancy(&pbresource.Tenancy{ + Partition: "default", + Namespace: "default", + }). + WithData(t, &pbdemo.Album{ + Name: "foo", + }). + WithOwner(&pbresource.ID{ + Type: pbdemo.ArtistType, + Tenancy: &pbresource.Tenancy{ + Partition: "default", + Namespace: "default", + }, + }). + Build() + + txn := idx.Txn() + require.NoError(t, txn.Insert(r1)) + txn.Commit() + + out, err := idx.Txn().Get(r1.Owner) + require.NoError(t, err) + prototest.AssertDeepEqual(t, r1, out) +} + +func TestSingleIDOrRefIndex(t *testing.T) { + getRef := indexersmock.NewGetSingleRefOrID(t) + + idx := SingleIDOrRefIndex("test", getRef.Execute) + + r1 := resourcetest.Resource(pbdemo.AlbumType, "foo").Build() + ref := &pbresource.Reference{ + Type: pbdemo.ArtistType, + Tenancy: &pbresource.Tenancy{ + Partition: "default", + Namespace: "default", + }, + Name: "foo", + } + + getRef.EXPECT().Execute(r1). + Return(ref). + Once() + + txn := idx.Txn() + require.NoError(t, txn.Insert(r1)) + txn.Commit() + + out, err := idx.Txn().Get(ref) + require.NoError(t, err) + prototest.AssertDeepEqual(t, r1, out) +} diff --git a/internal/controller/cache/indexers/indexersmock/mock_BoundReferences.go b/internal/controller/cache/indexers/indexersmock/mock_BoundReferences.go new file mode 100644 index 0000000000..aa8bf7b005 --- /dev/null +++ b/internal/controller/cache/indexers/indexersmock/mock_BoundReferences.go @@ -0,0 +1,122 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package indexersmock + +import ( + pbresource "github.com/hashicorp/consul/proto-public/pbresource" + mock "github.com/stretchr/testify/mock" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" +) + +// BoundReferences is an autogenerated mock type for the BoundReferences type +type BoundReferences struct { + mock.Mock +} + +type BoundReferences_Expecter struct { + mock *mock.Mock +} + +func (_m *BoundReferences) EXPECT() *BoundReferences_Expecter { + return &BoundReferences_Expecter{mock: &_m.Mock} +} + +// GetBoundReferences provides a mock function with given fields: +func (_m *BoundReferences) GetBoundReferences() []*pbresource.Reference { + ret := _m.Called() + + var r0 []*pbresource.Reference + if rf, ok := ret.Get(0).(func() []*pbresource.Reference); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*pbresource.Reference) + } + } + + return r0 +} + +// BoundReferences_GetBoundReferences_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetBoundReferences' +type BoundReferences_GetBoundReferences_Call struct { + *mock.Call +} + +// GetBoundReferences is a helper method to define mock.On call +func (_e *BoundReferences_Expecter) GetBoundReferences() *BoundReferences_GetBoundReferences_Call { + return &BoundReferences_GetBoundReferences_Call{Call: _e.mock.On("GetBoundReferences")} +} + +func (_c *BoundReferences_GetBoundReferences_Call) Run(run func()) *BoundReferences_GetBoundReferences_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *BoundReferences_GetBoundReferences_Call) Return(_a0 []*pbresource.Reference) *BoundReferences_GetBoundReferences_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *BoundReferences_GetBoundReferences_Call) RunAndReturn(run func() []*pbresource.Reference) *BoundReferences_GetBoundReferences_Call { + _c.Call.Return(run) + return _c +} + +// ProtoReflect provides a mock function with given fields: +func (_m *BoundReferences) ProtoReflect() protoreflect.Message { + ret := _m.Called() + + var r0 protoreflect.Message + if rf, ok := ret.Get(0).(func() protoreflect.Message); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(protoreflect.Message) + } + } + + return r0 +} + +// BoundReferences_ProtoReflect_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ProtoReflect' +type BoundReferences_ProtoReflect_Call struct { + *mock.Call +} + +// ProtoReflect is a helper method to define mock.On call +func (_e *BoundReferences_Expecter) ProtoReflect() *BoundReferences_ProtoReflect_Call { + return &BoundReferences_ProtoReflect_Call{Call: _e.mock.On("ProtoReflect")} +} + +func (_c *BoundReferences_ProtoReflect_Call) Run(run func()) *BoundReferences_ProtoReflect_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *BoundReferences_ProtoReflect_Call) Return(_a0 protoreflect.Message) *BoundReferences_ProtoReflect_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *BoundReferences_ProtoReflect_Call) RunAndReturn(run func() protoreflect.Message) *BoundReferences_ProtoReflect_Call { + _c.Call.Return(run) + return _c +} + +// NewBoundReferences creates a new instance of BoundReferences. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewBoundReferences(t interface { + mock.TestingT + Cleanup(func()) +}) *BoundReferences { + mock := &BoundReferences{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/controller/cache/indexers/indexersmock/mock_FromArgs.go b/internal/controller/cache/indexers/indexersmock/mock_FromArgs.go new file mode 100644 index 0000000000..bb74bbd61d --- /dev/null +++ b/internal/controller/cache/indexers/indexersmock/mock_FromArgs.go @@ -0,0 +1,95 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package indexersmock + +import mock "github.com/stretchr/testify/mock" + +// FromArgs is an autogenerated mock type for the FromArgs type +type FromArgs struct { + mock.Mock +} + +type FromArgs_Expecter struct { + mock *mock.Mock +} + +func (_m *FromArgs) EXPECT() *FromArgs_Expecter { + return &FromArgs_Expecter{mock: &_m.Mock} +} + +// Execute provides a mock function with given fields: args +func (_m *FromArgs) Execute(args ...interface{}) ([]byte, error) { + var _ca []interface{} + _ca = append(_ca, args...) + ret := _m.Called(_ca...) + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(...interface{}) ([]byte, error)); ok { + return rf(args...) + } + if rf, ok := ret.Get(0).(func(...interface{}) []byte); ok { + r0 = rf(args...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(...interface{}) error); ok { + r1 = rf(args...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FromArgs_Execute_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Execute' +type FromArgs_Execute_Call struct { + *mock.Call +} + +// Execute is a helper method to define mock.On call +// - args ...interface{} +func (_e *FromArgs_Expecter) Execute(args ...interface{}) *FromArgs_Execute_Call { + return &FromArgs_Execute_Call{Call: _e.mock.On("Execute", + append([]interface{}{}, args...)...)} +} + +func (_c *FromArgs_Execute_Call) Run(run func(args ...interface{})) *FromArgs_Execute_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]interface{}, len(args)-0) + for i, a := range args[0:] { + if a != nil { + variadicArgs[i] = a.(interface{}) + } + } + run(variadicArgs...) + }) + return _c +} + +func (_c *FromArgs_Execute_Call) Return(_a0 []byte, _a1 error) *FromArgs_Execute_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *FromArgs_Execute_Call) RunAndReturn(run func(...interface{}) ([]byte, error)) *FromArgs_Execute_Call { + _c.Call.Return(run) + return _c +} + +// NewFromArgs creates a new instance of FromArgs. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewFromArgs(t interface { + mock.TestingT + Cleanup(func()) +}) *FromArgs { + mock := &FromArgs{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/controller/cache/indexers/indexersmock/mock_GetSingleRefOrID.go b/internal/controller/cache/indexers/indexersmock/mock_GetSingleRefOrID.go new file mode 100644 index 0000000000..77ec942bf7 --- /dev/null +++ b/internal/controller/cache/indexers/indexersmock/mock_GetSingleRefOrID.go @@ -0,0 +1,80 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package indexersmock + +import ( + resource "github.com/hashicorp/consul/internal/resource" + pbresource "github.com/hashicorp/consul/proto-public/pbresource" + mock "github.com/stretchr/testify/mock" +) + +// GetSingleRefOrID is an autogenerated mock type for the GetSingleRefOrID type +type GetSingleRefOrID struct { + mock.Mock +} + +type GetSingleRefOrID_Expecter struct { + mock *mock.Mock +} + +func (_m *GetSingleRefOrID) EXPECT() *GetSingleRefOrID_Expecter { + return &GetSingleRefOrID_Expecter{mock: &_m.Mock} +} + +// Execute provides a mock function with given fields: _a0 +func (_m *GetSingleRefOrID) Execute(_a0 *pbresource.Resource) resource.ReferenceOrID { + ret := _m.Called(_a0) + + var r0 resource.ReferenceOrID + if rf, ok := ret.Get(0).(func(*pbresource.Resource) resource.ReferenceOrID); ok { + r0 = rf(_a0) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(resource.ReferenceOrID) + } + } + + return r0 +} + +// GetSingleRefOrID_Execute_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Execute' +type GetSingleRefOrID_Execute_Call struct { + *mock.Call +} + +// Execute is a helper method to define mock.On call +// - _a0 *pbresource.Resource +func (_e *GetSingleRefOrID_Expecter) Execute(_a0 interface{}) *GetSingleRefOrID_Execute_Call { + return &GetSingleRefOrID_Execute_Call{Call: _e.mock.On("Execute", _a0)} +} + +func (_c *GetSingleRefOrID_Execute_Call) Run(run func(_a0 *pbresource.Resource)) *GetSingleRefOrID_Execute_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*pbresource.Resource)) + }) + return _c +} + +func (_c *GetSingleRefOrID_Execute_Call) Return(_a0 resource.ReferenceOrID) *GetSingleRefOrID_Execute_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *GetSingleRefOrID_Execute_Call) RunAndReturn(run func(*pbresource.Resource) resource.ReferenceOrID) *GetSingleRefOrID_Execute_Call { + _c.Call.Return(run) + return _c +} + +// NewGetSingleRefOrID creates a new instance of GetSingleRefOrID. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewGetSingleRefOrID(t interface { + mock.TestingT + Cleanup(func()) +}) *GetSingleRefOrID { + mock := &GetSingleRefOrID{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/controller/cache/indexers/indexersmock/mock_MultiIndexer.go b/internal/controller/cache/indexers/indexersmock/mock_MultiIndexer.go new file mode 100644 index 0000000000..080b45d28b --- /dev/null +++ b/internal/controller/cache/indexers/indexersmock/mock_MultiIndexer.go @@ -0,0 +1,97 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package indexersmock + +import ( + resource "github.com/hashicorp/consul/internal/resource" + mock "github.com/stretchr/testify/mock" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" +) + +// MultiIndexer is an autogenerated mock type for the MultiIndexer type +type MultiIndexer[T protoreflect.ProtoMessage] struct { + mock.Mock +} + +type MultiIndexer_Expecter[T protoreflect.ProtoMessage] struct { + mock *mock.Mock +} + +func (_m *MultiIndexer[T]) EXPECT() *MultiIndexer_Expecter[T] { + return &MultiIndexer_Expecter[T]{mock: &_m.Mock} +} + +// Execute provides a mock function with given fields: r +func (_m *MultiIndexer[T]) Execute(r *resource.DecodedResource[T]) (bool, [][]byte, error) { + ret := _m.Called(r) + + var r0 bool + var r1 [][]byte + var r2 error + if rf, ok := ret.Get(0).(func(*resource.DecodedResource[T]) (bool, [][]byte, error)); ok { + return rf(r) + } + if rf, ok := ret.Get(0).(func(*resource.DecodedResource[T]) bool); ok { + r0 = rf(r) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(*resource.DecodedResource[T]) [][]byte); ok { + r1 = rf(r) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).([][]byte) + } + } + + if rf, ok := ret.Get(2).(func(*resource.DecodedResource[T]) error); ok { + r2 = rf(r) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// MultiIndexer_Execute_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Execute' +type MultiIndexer_Execute_Call[T protoreflect.ProtoMessage] struct { + *mock.Call +} + +// Execute is a helper method to define mock.On call +// - r *resource.DecodedResource[T] +func (_e *MultiIndexer_Expecter[T]) Execute(r interface{}) *MultiIndexer_Execute_Call[T] { + return &MultiIndexer_Execute_Call[T]{Call: _e.mock.On("Execute", r)} +} + +func (_c *MultiIndexer_Execute_Call[T]) Run(run func(r *resource.DecodedResource[T])) *MultiIndexer_Execute_Call[T] { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*resource.DecodedResource[T])) + }) + return _c +} + +func (_c *MultiIndexer_Execute_Call[T]) Return(_a0 bool, _a1 [][]byte, _a2 error) *MultiIndexer_Execute_Call[T] { + _c.Call.Return(_a0, _a1, _a2) + return _c +} + +func (_c *MultiIndexer_Execute_Call[T]) RunAndReturn(run func(*resource.DecodedResource[T]) (bool, [][]byte, error)) *MultiIndexer_Execute_Call[T] { + _c.Call.Return(run) + return _c +} + +// NewMultiIndexer creates a new instance of MultiIndexer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMultiIndexer[T protoreflect.ProtoMessage](t interface { + mock.TestingT + Cleanup(func()) +}) *MultiIndexer[T] { + mock := &MultiIndexer[T]{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/controller/cache/indexers/indexersmock/mock_RefOrIDFetcher.go b/internal/controller/cache/indexers/indexersmock/mock_RefOrIDFetcher.go new file mode 100644 index 0000000000..090f89d70c --- /dev/null +++ b/internal/controller/cache/indexers/indexersmock/mock_RefOrIDFetcher.go @@ -0,0 +1,80 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package indexersmock + +import ( + resource "github.com/hashicorp/consul/internal/resource" + mock "github.com/stretchr/testify/mock" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" +) + +// RefOrIDFetcher is an autogenerated mock type for the RefOrIDFetcher type +type RefOrIDFetcher[T protoreflect.ProtoMessage, V resource.ReferenceOrID] struct { + mock.Mock +} + +type RefOrIDFetcher_Expecter[T protoreflect.ProtoMessage, V resource.ReferenceOrID] struct { + mock *mock.Mock +} + +func (_m *RefOrIDFetcher[T, V]) EXPECT() *RefOrIDFetcher_Expecter[T, V] { + return &RefOrIDFetcher_Expecter[T, V]{mock: &_m.Mock} +} + +// Execute provides a mock function with given fields: _a0 +func (_m *RefOrIDFetcher[T, V]) Execute(_a0 *resource.DecodedResource[T]) []V { + ret := _m.Called(_a0) + + var r0 []V + if rf, ok := ret.Get(0).(func(*resource.DecodedResource[T]) []V); ok { + r0 = rf(_a0) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]V) + } + } + + return r0 +} + +// RefOrIDFetcher_Execute_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Execute' +type RefOrIDFetcher_Execute_Call[T protoreflect.ProtoMessage, V resource.ReferenceOrID] struct { + *mock.Call +} + +// Execute is a helper method to define mock.On call +// - _a0 *resource.DecodedResource[T] +func (_e *RefOrIDFetcher_Expecter[T, V]) Execute(_a0 interface{}) *RefOrIDFetcher_Execute_Call[T, V] { + return &RefOrIDFetcher_Execute_Call[T, V]{Call: _e.mock.On("Execute", _a0)} +} + +func (_c *RefOrIDFetcher_Execute_Call[T, V]) Run(run func(_a0 *resource.DecodedResource[T])) *RefOrIDFetcher_Execute_Call[T, V] { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*resource.DecodedResource[T])) + }) + return _c +} + +func (_c *RefOrIDFetcher_Execute_Call[T, V]) Return(_a0 []V) *RefOrIDFetcher_Execute_Call[T, V] { + _c.Call.Return(_a0) + return _c +} + +func (_c *RefOrIDFetcher_Execute_Call[T, V]) RunAndReturn(run func(*resource.DecodedResource[T]) []V) *RefOrIDFetcher_Execute_Call[T, V] { + _c.Call.Return(run) + return _c +} + +// NewRefOrIDFetcher creates a new instance of RefOrIDFetcher. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewRefOrIDFetcher[T protoreflect.ProtoMessage, V resource.ReferenceOrID](t interface { + mock.TestingT + Cleanup(func()) +}) *RefOrIDFetcher[T, V] { + mock := &RefOrIDFetcher[T, V]{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/controller/cache/indexers/indexersmock/mock_SingleIndexer.go b/internal/controller/cache/indexers/indexersmock/mock_SingleIndexer.go new file mode 100644 index 0000000000..38cdfb71c4 --- /dev/null +++ b/internal/controller/cache/indexers/indexersmock/mock_SingleIndexer.go @@ -0,0 +1,97 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package indexersmock + +import ( + resource "github.com/hashicorp/consul/internal/resource" + mock "github.com/stretchr/testify/mock" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" +) + +// SingleIndexer is an autogenerated mock type for the SingleIndexer type +type SingleIndexer[T protoreflect.ProtoMessage] struct { + mock.Mock +} + +type SingleIndexer_Expecter[T protoreflect.ProtoMessage] struct { + mock *mock.Mock +} + +func (_m *SingleIndexer[T]) EXPECT() *SingleIndexer_Expecter[T] { + return &SingleIndexer_Expecter[T]{mock: &_m.Mock} +} + +// Execute provides a mock function with given fields: r +func (_m *SingleIndexer[T]) Execute(r *resource.DecodedResource[T]) (bool, []byte, error) { + ret := _m.Called(r) + + var r0 bool + var r1 []byte + var r2 error + if rf, ok := ret.Get(0).(func(*resource.DecodedResource[T]) (bool, []byte, error)); ok { + return rf(r) + } + if rf, ok := ret.Get(0).(func(*resource.DecodedResource[T]) bool); ok { + r0 = rf(r) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(*resource.DecodedResource[T]) []byte); ok { + r1 = rf(r) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).([]byte) + } + } + + if rf, ok := ret.Get(2).(func(*resource.DecodedResource[T]) error); ok { + r2 = rf(r) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// SingleIndexer_Execute_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Execute' +type SingleIndexer_Execute_Call[T protoreflect.ProtoMessage] struct { + *mock.Call +} + +// Execute is a helper method to define mock.On call +// - r *resource.DecodedResource[T] +func (_e *SingleIndexer_Expecter[T]) Execute(r interface{}) *SingleIndexer_Execute_Call[T] { + return &SingleIndexer_Execute_Call[T]{Call: _e.mock.On("Execute", r)} +} + +func (_c *SingleIndexer_Execute_Call[T]) Run(run func(r *resource.DecodedResource[T])) *SingleIndexer_Execute_Call[T] { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*resource.DecodedResource[T])) + }) + return _c +} + +func (_c *SingleIndexer_Execute_Call[T]) Return(_a0 bool, _a1 []byte, _a2 error) *SingleIndexer_Execute_Call[T] { + _c.Call.Return(_a0, _a1, _a2) + return _c +} + +func (_c *SingleIndexer_Execute_Call[T]) RunAndReturn(run func(*resource.DecodedResource[T]) (bool, []byte, error)) *SingleIndexer_Execute_Call[T] { + _c.Call.Return(run) + return _c +} + +// NewSingleIndexer creates a new instance of SingleIndexer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewSingleIndexer[T protoreflect.ProtoMessage](t interface { + mock.TestingT + Cleanup(func()) +}) *SingleIndexer[T] { + mock := &SingleIndexer[T]{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/controller/cache/indexers/ref_indexer.go b/internal/controller/cache/indexers/ref_indexer.go new file mode 100644 index 0000000000..89562582f2 --- /dev/null +++ b/internal/controller/cache/indexers/ref_indexer.go @@ -0,0 +1,37 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package indexers + +import ( + "github.com/hashicorp/consul/internal/controller/cache/index" + "github.com/hashicorp/consul/internal/resource" + "github.com/hashicorp/consul/proto-public/pbresource" + "google.golang.org/protobuf/proto" +) + +//go:generate mockery --name RefOrIDFetcher --with-expecter +type RefOrIDFetcher[T proto.Message, V resource.ReferenceOrID] func(*resource.DecodedResource[T]) []V + +func RefOrIDIndex[T proto.Message, V resource.ReferenceOrID](name string, fetch RefOrIDFetcher[T, V]) *index.Index { + return DecodedMultiIndexer[T](name, index.ReferenceOrIDFromArgs, func(r *resource.DecodedResource[T]) (bool, [][]byte, error) { + refs := fetch(r) + indexes := make([][]byte, len(refs)) + for idx, ref := range refs { + indexes[idx] = index.IndexFromRefOrID(ref) + } + + return len(indexes) > 0, indexes, nil + }) +} + +type BoundReferences interface { + GetBoundReferences() []*pbresource.Reference + proto.Message +} + +func BoundRefsIndex[T BoundReferences](name string) *index.Index { + return RefOrIDIndex[T](name, func(res *resource.DecodedResource[T]) []*pbresource.Reference { + return res.Data.GetBoundReferences() + }) +} diff --git a/internal/controller/cache/indexers/ref_indexer_test.go b/internal/controller/cache/indexers/ref_indexer_test.go new file mode 100644 index 0000000000..cfd875be42 --- /dev/null +++ b/internal/controller/cache/indexers/ref_indexer_test.go @@ -0,0 +1,106 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package indexers + +import ( + "testing" + + "github.com/hashicorp/consul/internal/controller/cache/indexers/indexersmock" + "github.com/hashicorp/consul/internal/resource/resourcetest" + pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v2beta1" + pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v2beta1" + "github.com/hashicorp/consul/proto-public/pbresource" + pbdemo "github.com/hashicorp/consul/proto/private/pbdemo/v1" + "github.com/hashicorp/consul/proto/private/prototest" + "github.com/stretchr/testify/require" +) + +func TestRefOrIDIndex(t *testing.T) { + ref1 := &pbresource.Reference{ + Type: pbdemo.AlbumType, + Tenancy: &pbresource.Tenancy{ + Partition: "default", + Namespace: "default", + }, + Name: "foo", + } + + ref2 := &pbresource.Reference{ + Type: pbdemo.AlbumType, + Tenancy: &pbresource.Tenancy{ + Partition: "default", + Namespace: "default", + }, + Name: "bar", + } + + r1 := resourcetest.Resource(pbdemo.AlbumType, "foo"). + WithData(t, &pbdemo.Album{Name: "foo"}). + Build() + + dec := resourcetest.MustDecode[*pbdemo.Album](t, r1) + + refs := indexersmock.NewRefOrIDFetcher[*pbdemo.Album, *pbresource.Reference](t) + + idx := RefOrIDIndex("test", refs.Execute) + + refs.EXPECT().Execute(dec). + Return([]*pbresource.Reference{ref1, ref2}). + Once() + + txn := idx.Txn() + require.NoError(t, txn.Insert(r1)) + txn.Commit() + + out, err := idx.Txn().Get(ref1) + require.NoError(t, err) + prototest.AssertDeepEqual(t, r1, out) + + out, err = idx.Txn().Get(ref2) + require.NoError(t, err) + prototest.AssertDeepEqual(t, r1, out) +} + +func TestBoundRefsIndex(t *testing.T) { + ref1 := &pbresource.Reference{ + Type: pbcatalog.ServiceType, + Tenancy: &pbresource.Tenancy{ + Partition: "default", + Namespace: "default", + }, + Name: "api", + } + + ref2 := &pbresource.Reference{ + Type: pbcatalog.ServiceType, + Tenancy: &pbresource.Tenancy{ + Partition: "default", + Namespace: "default", + }, + Name: "api-2", + } + + r1 := resourcetest.Resource(pbmesh.ComputedRoutesType, "api"). + WithData(t, &pbmesh.ComputedRoutes{ + BoundReferences: []*pbresource.Reference{ + ref1, + ref2, + }, + }). + Build() + + idx := BoundRefsIndex[*pbmesh.ComputedRoutes]("test") + + txn := idx.Txn() + require.NoError(t, txn.Insert(r1)) + txn.Commit() + + out, err := idx.Txn().Get(ref1) + require.NoError(t, err) + prototest.AssertDeepEqual(t, r1, out) + + out, err = idx.Txn().Get(ref2) + require.NoError(t, err) + prototest.AssertDeepEqual(t, r1, out) +} diff --git a/internal/controller/cache/kind.go b/internal/controller/cache/kind.go new file mode 100644 index 0000000000..4d9aa4d4f4 --- /dev/null +++ b/internal/controller/cache/kind.go @@ -0,0 +1,188 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package cache + +import ( + "sync" + + "github.com/hashicorp/consul/internal/controller/cache/index" + "github.com/hashicorp/consul/internal/controller/cache/indexers" + "github.com/hashicorp/consul/proto-public/pbresource" +) + +const IDIndex = "id" + +type kindIndices struct { + mu sync.RWMutex + + it unversionedType + + indices map[string]*index.Index +} + +func newKindIndices() *kindIndices { + kind := &kindIndices{ + indices: make(map[string]*index.Index), + } + + // add the id index + kind.indices[IDIndex] = indexers.IDIndex(IDIndex, index.IndexRequired) + + return kind +} + +func (k *kindIndices) addIndex(i *index.Index) error { + _, found := k.indices[i.Name()] + if found { + return DuplicateIndexError{name: i.Name()} + } + + k.indices[i.Name()] = i + return nil +} + +func (k *kindIndices) get(indexName string, args ...any) (*pbresource.Resource, error) { + k.mu.RLock() + defer k.mu.RUnlock() + + idx, err := k.getIndex(indexName) + if err != nil { + return nil, err + } + + r, err := idx.Txn().Get(args...) + if err != nil { + return nil, IndexError{err: err, name: indexName} + } + return r, nil +} + +func (k *kindIndices) listIterator(indexName string, args ...any) (ResourceIterator, error) { + k.mu.RLock() + defer k.mu.RUnlock() + + idx, err := k.getIndex(indexName) + if err != nil { + return nil, err + } + + iter, err := idx.Txn().ListIterator(args...) + if err != nil { + return nil, IndexError{err: err, name: indexName} + } + return iter, nil +} + +func (k *kindIndices) parentsIterator(indexName string, args ...any) (ResourceIterator, error) { + k.mu.RLock() + defer k.mu.RUnlock() + idx, err := k.getIndex(indexName) + if err != nil { + return nil, err + } + + iter, err := idx.Txn().ParentsIterator(args...) + if err != nil { + return nil, IndexError{err: err, name: indexName} + } + return iter, nil +} + +func (k *kindIndices) insert(r *pbresource.Resource) error { + k.mu.Lock() + defer k.mu.Unlock() + + idx, err := k.getIndex(IDIndex) + if err != nil { + return err + } + + existing, err := idx.Txn().Get(r.Id) + if err != nil { + return err + } + + commit := false + for name, idx := range k.indices { + txn := idx.Txn() + + // Delete the previous version of the resource from the index. + if existing != nil { + if err := txn.Delete(existing); err != nil { + return IndexError{name: name, err: err} + } + } + + // Now insert the new version into the index. + err := txn.Insert(r) + if err != nil { + return IndexError{name: name, err: err} + } + // commit all radix trees once we know all index applies were successful. This is + // still while holding the big write lock for this resource type so the order that + // the radix tree updates occur shouldn't matter. + defer func() { + if commit { + txn.Commit() + } + }() + } + + // set commit to true so that the deferred funcs will commit all the radix tree transactions + commit = true + + return nil +} + +func (k *kindIndices) delete(r *pbresource.Resource) error { + k.mu.Lock() + defer k.mu.Unlock() + + idx, err := k.getIndex(IDIndex) + if err != nil { + return err + } + + idTxn := idx.Txn() + + existing, err := idTxn.Get(r.Id) + if err != nil { + return err + } + if existing == nil { + return nil + } + + commit := false + for name, idx := range k.indices { + txn := idx.Txn() + + if err := txn.Delete(existing); err != nil { + return IndexError{name: name, err: err} + } + + // commit all radix trees once we know all index applies were successful. This is + // still while holding the big write lock for this resource type so the order that + // the radix tree updates occur shouldn't matter. + defer func() { + if commit { + txn.Commit() + } + }() + } + + // set commit to true so that the deferred txn commits will get executed + commit = true + + return nil +} + +func (k *kindIndices) getIndex(name string) (*index.Index, error) { + idx, ok := k.indices[name] + if !ok { + return nil, CacheTypeError{err: IndexNotFoundError{name: name}, it: k.it} + } + + return idx, nil +} diff --git a/internal/controller/cache/kind_test.go b/internal/controller/cache/kind_test.go new file mode 100644 index 0000000000..60ad60ebb1 --- /dev/null +++ b/internal/controller/cache/kind_test.go @@ -0,0 +1,202 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package cache + +import ( + "errors" + "testing" + + "github.com/hashicorp/consul/internal/controller/cache/index" + "github.com/hashicorp/consul/internal/resource" + "github.com/hashicorp/consul/internal/resource/resourcetest" + "github.com/hashicorp/consul/proto-public/pbresource" + pbdemo "github.com/hashicorp/consul/proto/private/pbdemo/v1" + "github.com/hashicorp/consul/proto/private/prototest" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" +) + +var injectedError = errors.New("test-error") + +type argsErrorIndexer struct{} + +func (i argsErrorIndexer) FromArgs(args ...any) ([]byte, error) { + return nil, injectedError +} + +func (i argsErrorIndexer) FromResource(r *pbresource.Resource) (bool, []byte, error) { + return true, index.IndexFromRefOrID(r.GetId()), nil +} + +type resourceErrorIndexer struct{} + +func (i resourceErrorIndexer) FromArgs(args ...any) ([]byte, error) { + return index.ReferenceOrIDFromArgs(args...) +} + +func (i resourceErrorIndexer) FromResource(r *pbresource.Resource) (bool, []byte, error) { + return false, nil, injectedError +} + +func TestKindIndices(t *testing.T) { + suite.Run(t, &kindSuite{}) +} + +type kindSuite struct { + suite.Suite + k *kindIndices + + album1 *pbresource.Resource + album2 *pbresource.Resource + album3 *pbresource.Resource + album4 *pbresource.Resource +} + +func (suite *kindSuite) SetupTest() { + suite.k = newKindIndices() + + require.NoError(suite.T(), suite.k.addIndex(namePrefixIndexer())) + require.NoError(suite.T(), suite.k.addIndex(releaseYearIndexer())) + require.NoError(suite.T(), suite.k.addIndex(tracksIndexer())) + + suite.album1 = resourcetest.Resource(pbdemo.AlbumType, "one"). + WithTenancy(resource.DefaultNamespacedTenancy()). + WithData(suite.T(), &pbdemo.Album{ + Name: "one", + YearOfRelease: 2023, + Tracks: []string{"foo", "bar", "baz"}, + }). + Build() + + suite.album2 = resourcetest.Resource(pbdemo.AlbumType, "two"). + WithTenancy(resource.DefaultNamespacedTenancy()). + WithData(suite.T(), &pbdemo.Album{ + Name: "two", + YearOfRelease: 2023, + Tracks: []string{"fangorn", "zoo"}, + }). + Build() + + suite.album3 = resourcetest.Resource(pbdemo.AlbumType, "third"). + WithTenancy(resource.DefaultNamespacedTenancy()). + WithData(suite.T(), &pbdemo.Album{ + Name: "foo", + YearOfRelease: 2022, + Tracks: []string{"blah", "something", "else"}, + }). + Build() + + suite.album4 = resourcetest.Resource(pbdemo.AlbumType, "four"). + WithTenancy(resource.DefaultNamespacedTenancy()). + WithData(suite.T(), &pbdemo.Album{ + Name: "food", + YearOfRelease: 2020, + Tracks: []string{"nothing", "food"}, + }). + Build() + + require.NoError(suite.T(), suite.k.insert(suite.album1)) + require.NoError(suite.T(), suite.k.insert(suite.album2)) + require.NoError(suite.T(), suite.k.insert(suite.album3)) + require.NoError(suite.T(), suite.k.insert(suite.album4)) +} + +func (suite *kindSuite) TestGet() { + res, err := suite.k.get("id", suite.album1.Id) + require.NoError(suite.T(), err) + prototest.AssertDeepEqual(suite.T(), suite.album1, res) + + res, err = suite.k.get("year", int32(2022)) + require.NoError(suite.T(), err) + prototest.AssertDeepEqual(suite.T(), suite.album3, res) + + res, err = suite.k.get("tracks", "fangorn") + require.NoError(suite.T(), err) + prototest.AssertDeepEqual(suite.T(), suite.album2, res) +} + +func (suite *kindSuite) TestGet_IndexNotFound() { + res, err := suite.k.get("blah", suite.album1.Id) + require.Error(suite.T(), err) + require.ErrorIs(suite.T(), err, IndexNotFoundError{name: "blah"}) + require.Nil(suite.T(), res) +} + +func (suite *kindSuite) TestList() { + resources, err := expandIterator(suite.k.listIterator("year", int32(2023))) + require.NoError(suite.T(), err) + prototest.AssertElementsMatch(suite.T(), []*pbresource.Resource{suite.album1, suite.album2}, resources) + + resources, err = expandIterator(suite.k.listIterator("tracks", "f", true)) + require.NoError(suite.T(), err) + prototest.AssertElementsMatch(suite.T(), []*pbresource.Resource{suite.album1, suite.album2, suite.album4}, resources) +} + +func (suite *kindSuite) TestList_IndexNotFound() { + res, err := expandIterator(suite.k.listIterator("blah", suite.album1.Id)) + require.Error(suite.T(), err) + require.ErrorIs(suite.T(), err, IndexNotFoundError{name: "blah"}) + require.Nil(suite.T(), res) +} + +func (suite *kindSuite) TestParents() { + resources, err := expandIterator(suite.k.parentsIterator("name_prefix", "food")) + require.NoError(suite.T(), err) + prototest.AssertElementsMatch(suite.T(), []*pbresource.Resource{suite.album3, suite.album4}, resources) +} + +func (suite *kindSuite) TestParents_IndexNotFound() { + res, err := expandIterator(suite.k.parentsIterator("blah", suite.album1.Id)) + require.Error(suite.T(), err) + require.ErrorIs(suite.T(), err, IndexNotFoundError{name: "blah"}) + require.Nil(suite.T(), res) +} +func (suite *kindSuite) TestDelete() { + err := suite.k.delete(suite.album1) + require.NoError(suite.T(), err) + + res, err := suite.k.get("id", suite.album1.Id) + require.NoError(suite.T(), err) + require.Nil(suite.T(), res) + + resources, err := expandIterator(suite.k.listIterator("year", int32(2023))) + require.NoError(suite.T(), err) + prototest.AssertElementsMatch(suite.T(), []*pbresource.Resource{suite.album2}, resources) + + resources, err = expandIterator(suite.k.parentsIterator("name_prefix", "onesie")) + require.NoError(suite.T(), err) + require.Nil(suite.T(), resources) +} + +func (suite *kindSuite) TestInsertIndexError() { + err := suite.k.insert( + resourcetest.Resource(pbdemo.ConceptType, "foo"). + WithTenancy(resource.DefaultNamespacedTenancy()). + WithData(suite.T(), &pbdemo.Concept{}). + Build()) + + require.Error(suite.T(), err) + require.ErrorAs(suite.T(), err, &IndexError{}) +} + +func (suite *kindSuite) TestGetIndexError() { + val, err := suite.k.get("year", "blah") + require.Error(suite.T(), err) + require.ErrorAs(suite.T(), err, &IndexError{}) + require.Nil(suite.T(), val) +} + +func (suite *kindSuite) TestListIteratorIndexError() { + vals, err := suite.k.listIterator("year", "blah") + require.Error(suite.T(), err) + require.ErrorAs(suite.T(), err, &IndexError{}) + require.Nil(suite.T(), vals) +} + +func (suite *kindSuite) TestParentsIteratorIndexError() { + vals, err := suite.k.parentsIterator("year", "blah") + require.Error(suite.T(), err) + require.ErrorAs(suite.T(), err, &IndexError{}) + require.Nil(suite.T(), vals) +} diff --git a/internal/controller/cache/testdata/CacheTypeError.golden b/internal/controller/cache/testdata/CacheTypeError.golden new file mode 100644 index 0000000000..92f3253807 --- /dev/null +++ b/internal/controller/cache/testdata/CacheTypeError.golden @@ -0,0 +1 @@ +operation on resource type something.else failed: fake test error \ No newline at end of file diff --git a/internal/controller/cache/testdata/DuplicateIndexError.golden b/internal/controller/cache/testdata/DuplicateIndexError.golden new file mode 100644 index 0000000000..3820128104 --- /dev/null +++ b/internal/controller/cache/testdata/DuplicateIndexError.golden @@ -0,0 +1 @@ +Index with name "addresses" is already defined. \ No newline at end of file diff --git a/internal/controller/cache/testdata/DuplicateQueryError.golden b/internal/controller/cache/testdata/DuplicateQueryError.golden new file mode 100644 index 0000000000..2a7393c173 --- /dev/null +++ b/internal/controller/cache/testdata/DuplicateQueryError.golden @@ -0,0 +1 @@ +Query with name "addresses" is already defined. \ No newline at end of file diff --git a/internal/controller/cache/testdata/IndexError.golden b/internal/controller/cache/testdata/IndexError.golden new file mode 100644 index 0000000000..8f52e92f2f --- /dev/null +++ b/internal/controller/cache/testdata/IndexError.golden @@ -0,0 +1 @@ +operation on index "foo" failed: fake test error \ No newline at end of file diff --git a/internal/controller/cache/testdata/IndexNotFound.golden b/internal/controller/cache/testdata/IndexNotFound.golden new file mode 100644 index 0000000000..9f5e62f48d --- /dev/null +++ b/internal/controller/cache/testdata/IndexNotFound.golden @@ -0,0 +1 @@ +No index with name "fake" exists \ No newline at end of file diff --git a/internal/controller/cache/testdata/QueryNotFound.golden b/internal/controller/cache/testdata/QueryNotFound.golden new file mode 100644 index 0000000000..ede6b89e5d --- /dev/null +++ b/internal/controller/cache/testdata/QueryNotFound.golden @@ -0,0 +1 @@ +No query with name "fake" exists \ No newline at end of file diff --git a/internal/controller/cache/testdata/QueryRequired.golden b/internal/controller/cache/testdata/QueryRequired.golden new file mode 100644 index 0000000000..41aeda42d1 --- /dev/null +++ b/internal/controller/cache/testdata/QueryRequired.golden @@ -0,0 +1 @@ +A non-nil query function was not specified \ No newline at end of file diff --git a/internal/controller/controller.go b/internal/controller/controller.go index 97955db6a3..5585a0e109 100644 --- a/internal/controller/controller.go +++ b/internal/controller/controller.go @@ -5,244 +5,288 @@ package controller import ( "context" - "errors" "fmt" + "strings" "time" - "github.com/hashicorp/go-hclog" - "golang.org/x/sync/errgroup" - - "github.com/hashicorp/consul/agent/consul/controller/queue" + "github.com/hashicorp/consul/internal/controller/cache" + "github.com/hashicorp/consul/internal/controller/cache/index" "github.com/hashicorp/consul/internal/resource" "github.com/hashicorp/consul/proto-public/pbresource" + "github.com/hashicorp/go-hclog" ) -// controllerRunner contains the actual implementation of running a controller -// including creating watches, calling the reconciler, handling retries, etc. -type controllerRunner struct { - ctrl Controller - client pbresource.ResourceServiceClient - logger hclog.Logger -} - -func (c *controllerRunner) run(ctx context.Context) error { - c.logger.Debug("controller running") - defer c.logger.Debug("controller stopping") - - group, groupCtx := errgroup.WithContext(ctx) - recQueue := runQueue[Request](groupCtx, c.ctrl) - - // Managed Type Events → Reconciliation Queue - group.Go(func() error { - return c.watch(groupCtx, c.ctrl.managedType, func(res *pbresource.Resource) { - recQueue.Add(Request{ID: res.Id}) - }) - }) - - for _, w := range c.ctrl.watches { - mapQueue := runQueue[mapperRequest](groupCtx, c.ctrl) - watcher := w - // Watched Type Events → Mapper Queue - group.Go(func() error { - return c.watch(groupCtx, watcher.watchedType, func(res *pbresource.Resource) { - mapQueue.Add(mapperRequest{res: res}) - }) - }) - - // Mapper Queue → Mapper → Reconciliation Queue - group.Go(func() error { - return c.runMapper(groupCtx, watcher, mapQueue, recQueue, func(ctx context.Context, runtime Runtime, itemType queue.ItemType) ([]Request, error) { - return watcher.mapper(ctx, runtime, itemType.(mapperRequest).res) - }) - }) - } - - for _, cw := range c.ctrl.customWatches { - customMapQueue := runQueue[Event](groupCtx, c.ctrl) - watcher := cw - // Custom Events → Mapper Queue - group.Go(func() error { - return watcher.source.Watch(groupCtx, func(e Event) { - customMapQueue.Add(e) - }) - }) - - // Mapper Queue → Mapper → Reconciliation Queue - group.Go(func() error { - return c.runCustomMapper(groupCtx, watcher, customMapQueue, recQueue, func(ctx context.Context, runtime Runtime, itemType queue.ItemType) ([]Request, error) { - return watcher.mapper(ctx, runtime, itemType.(Event)) - }) - }) - } - - // Reconciliation Queue → Reconciler - group.Go(func() error { - return c.runReconciler(groupCtx, recQueue) - }) - - return group.Wait() -} - -func runQueue[T queue.ItemType](ctx context.Context, ctrl Controller) queue.WorkQueue[T] { - base, max := ctrl.backoff() - return queue.RunWorkQueue[T](ctx, base, max) -} - -func (c *controllerRunner) watch(ctx context.Context, typ *pbresource.Type, add func(*pbresource.Resource)) error { - wl, err := c.client.WatchList(ctx, &pbresource.WatchListRequest{ - Type: typ, - }) - if err != nil { - c.logger.Error("failed to create watch", "error", err) - return err - } - - for { - event, err := wl.Recv() - if err != nil { - c.logger.Warn("error received from watch", "error", err) - return err - } - add(event.Resource) - } -} - -func (c *controllerRunner) runMapper( +// DependencyMapper is called when a dependency watched via WithWatch is changed +// to determine which of the controller's managed resources need to be reconciled. +type DependencyMapper func( ctx context.Context, - w watch, - from queue.WorkQueue[mapperRequest], - to queue.WorkQueue[Request], - mapper func(ctx context.Context, runtime Runtime, itemType queue.ItemType) ([]Request, error), -) error { - logger := c.logger.With("watched_resource_type", resource.ToGVK(w.watchedType)) + rt Runtime, + res *pbresource.Resource, +) ([]Request, error) - for { - item, shutdown := from.Get() - if shutdown { - return nil - } +// Controller runs a reconciliation loop to respond to changes in resources and +// their dependencies. It is heavily inspired by Kubernetes' controller pattern: +// https://kubernetes.io/docs/concepts/architecture/controller/ +// +// Use the builder methods in this package (starting with NewController) to construct +// a controller, and then pass it to a Manager to be executed. +type Controller struct { + name string + reconciler Reconciler + managedTypeWatch *watch + watches map[string]*watch + queries map[string]cache.Query + customWatches []customWatch + placement Placement + baseBackoff time.Duration + maxBackoff time.Duration + logger hclog.Logger +} - if err := c.doMap(ctx, mapper, to, item, logger); err != nil { - from.AddRateLimited(item) - from.Done(item) - continue - } +// NewController creates a controller that is setup to watched the managed type. +// Extra cache indexes may be provided as well and these indexes will be automatically managed. +// Typically, further calls to other builder methods will be needed to fully configure +// the controller such as using WithReconcile to define the the code that will be called +// when the managed resource needs reconcilation. +func NewController(name string, managedType *pbresource.Type, indexes ...*index.Index) *Controller { + w := &watch{ + watchedType: managedType, + indexes: make(map[string]*index.Index), + } - from.Forget(item) - from.Done(item) + for _, idx := range indexes { + w.addIndex(idx) + } + + return &Controller{ + name: name, + managedTypeWatch: w, + watches: make(map[string]*watch), + queries: make(map[string]cache.Query), } } -func (c *controllerRunner) runCustomMapper( - ctx context.Context, - cw customWatch, - from queue.WorkQueue[Event], - to queue.WorkQueue[Request], - mapper func(ctx context.Context, runtime Runtime, itemType queue.ItemType) ([]Request, error), -) error { - logger := c.logger.With("watched_event", cw.source) +// WithReconciler changes the controller's reconciler. +func (ctl *Controller) WithReconciler(reconciler Reconciler) *Controller { + if reconciler == nil { + panic("reconciler must not be nil") + } - for { - item, shutdown := from.Get() - if shutdown { - return nil + ctl.reconciler = reconciler + return ctl +} + +// WithWatch enables watching of the specified resource type and mapping it to the managed type +// via the provided DependencyMapper. Extra cache indexes to calculate on the watched type +// may also be provided. +func (ctl *Controller) WithWatch(watchedType *pbresource.Type, mapper DependencyMapper, indexes ...*index.Index) *Controller { + key := resource.ToGVK(watchedType) + + _, alreadyWatched := ctl.watches[key] + if alreadyWatched { + panic(fmt.Sprintf("resource type %q already has a configured watch", key)) + } + + w := newWatch(watchedType, mapper) + + for _, idx := range indexes { + w.addIndex(idx) + } + + ctl.watches[key] = w + + return ctl +} + +// WithQuery will add a named query to the controllers cache for usage during reconcile or in dependency mappers +func (ctl *Controller) WithQuery(queryName string, fn cache.Query) *Controller { + _, duplicate := ctl.queries[queryName] + if duplicate { + panic(fmt.Sprintf("a predefined cache query with name %q already exists", queryName)) + } + + ctl.queries[queryName] = fn + return ctl +} + +// WithCustomWatch adds a new custom watch. Custom watches do not affect the controller cache. +func (ctl *Controller) WithCustomWatch(source *Source, mapper CustomDependencyMapper) *Controller { + if source == nil { + panic("source must not be nil") + } + + if mapper == nil { + panic("mapper must not be nil") + } + + ctl.customWatches = append(ctl.customWatches, customWatch{source, mapper}) + return ctl +} + +// WithLogger changes the controller's logger. +func (ctl *Controller) WithLogger(logger hclog.Logger) *Controller { + if logger == nil { + panic("logger must not be nil") + } + + ctl.logger = logger + return ctl +} + +// WithBackoff changes the base and maximum backoff values for the controller's +// retry rate limiter. +func (ctl *Controller) WithBackoff(base, max time.Duration) *Controller { + ctl.baseBackoff = base + ctl.maxBackoff = max + return ctl +} + +// WithPlacement changes where and how many replicas of the controller will run. +// In the majority of cases, the default placement (one leader elected instance +// per cluster) is the most appropriate and you shouldn't need to override it. +func (ctl *Controller) WithPlacement(placement Placement) *Controller { + ctl.placement = placement + return ctl +} + +// buildCache will construct a controller Cache given the watches/indexes that have +// been added to the controller. This is mainly to be used by the TestController and +// Manager when setting up how things +func (ctl *Controller) buildCache() cache.Cache { + c := cache.New() + + addWatchToCache(c, ctl.managedTypeWatch) + for _, w := range ctl.watches { + addWatchToCache(c, w) + } + + for name, query := range ctl.queries { + if err := c.AddQuery(name, query); err != nil { + panic(err) } + } - if err := c.doMap(ctx, mapper, to, item, logger); err != nil { - from.AddRateLimited(item) - from.Done(item) - continue + return c +} + +// String returns a textual description of the controller, useful for debugging. +func (ctl *Controller) String() string { + watchedTypes := make([]string, 0, len(ctl.watches)) + for watchedType := range ctl.watches { + watchedTypes = append(watchedTypes, watchedType) + } + base, max := ctl.backoff() + return fmt.Sprintf( + ", placement=%s>", + resource.ToGVK(ctl.managedTypeWatch.watchedType), + strings.Join(watchedTypes, ", "), + base, max, + ctl.placement, + ) +} + +func (ctl *Controller) backoff() (time.Duration, time.Duration) { + base := ctl.baseBackoff + if base == 0 { + base = 5 * time.Millisecond + } + max := ctl.maxBackoff + if max == 0 { + max = 1000 * time.Second + } + return base, max +} + +func (ctl *Controller) buildLogger(defaultLogger hclog.Logger) hclog.Logger { + logger := defaultLogger + if ctl.logger != nil { + logger = ctl.logger + } + + return logger.With("controller", ctl.name, "managed_type", resource.ToGVK(ctl.managedTypeWatch.watchedType)) +} + +func addWatchToCache(c cache.Cache, w *watch) { + c.AddType(w.watchedType) + for _, index := range w.indexes { + if err := c.AddIndex(w.watchedType, index); err != nil { + panic(err) } - - from.Forget(item) - from.Done(item) } } -func (c *controllerRunner) doMap(ctx context.Context, mapper func(ctx context.Context, runtime Runtime, itemType queue.ItemType) ([]Request, error), to queue.WorkQueue[Request], item queue.ItemType, logger hclog.Logger) error { - var reqs []Request - if err := c.handlePanic(func() error { - var err error - reqs, err = mapper(ctx, c.runtime(), item) - return err - }); err != nil { - return err - } +// Placement determines where and how many replicas of the controller will run. +type Placement int - for _, r := range reqs { - if !resource.EqualType(r.ID.Type, c.ctrl.managedType) { - logger.Error("dependency mapper returned request for a resource of the wrong type", - "type_expected", resource.ToGVK(c.ctrl.managedType), - "type_got", resource.ToGVK(r.ID.Type), - ) - continue - } - to.Add(r) +const ( + // PlacementSingleton ensures there is a single, leader-elected, instance of + // the controller running in the cluster at any time. It's the default and is + // suitable for most use-cases. + PlacementSingleton Placement = iota + + // PlacementEachServer ensures there is a replica of the controller running on + // each server in the cluster. It is useful for cases where the controller is + // responsible for applying some configuration resource to the server whenever + // it changes (e.g. rate-limit configuration). Generally, controllers in this + // placement mode should not modify resources. + PlacementEachServer +) + +// String satisfies the fmt.Stringer interface. +func (p Placement) String() string { + switch p { + case PlacementSingleton: + return "singleton" + case PlacementEachServer: + return "each-server" } - return nil + panic(fmt.Sprintf("unknown placement %d", p)) } -func (c *controllerRunner) runReconciler(ctx context.Context, queue queue.WorkQueue[Request]) error { - for { - req, shutdown := queue.Get() - if shutdown { - return nil - } - - c.logger.Trace("handling request", "request", req) - err := c.handlePanic(func() error { - return c.ctrl.reconciler.Reconcile(ctx, c.runtime(), req) - }) - if err == nil { - queue.Forget(req) - } else { - var requeueAfter RequeueAfterError - if errors.As(err, &requeueAfter) { - queue.Forget(req) - queue.AddAfter(req, time.Duration(requeueAfter)) - } else { - queue.AddRateLimited(req) - } - } - queue.Done(req) - } +// Reconciler implements the business logic of a controller. +type Reconciler interface { + // Reconcile the resource identified by req.ID. + Reconcile(ctx context.Context, rt Runtime, req Request) error } -func (c *controllerRunner) handlePanic(fn func() error) (err error) { - defer func() { - if r := recover(); r != nil { - stack := hclog.Stacktrace() - c.logger.Error("controller panic", - "panic", r, - "stack", stack, - ) - err = fmt.Errorf("panic [recovered]: %v", r) - return - } - }() +// RequeueAfterError is an error that allows a Reconciler to override the +// exponential backoff behavior of the Controller, rather than applying +// the backoff algorithm, returning a RequeueAfterError will cause the +// Controller to reschedule the Request at a given time in the future. +type RequeueAfterError time.Duration - return fn() +// Error implements the error interface. +func (r RequeueAfterError) Error() string { + return fmt.Sprintf("requeue at %s", time.Duration(r)) } -func (c *controllerRunner) runtime() Runtime { - return Runtime{ - Client: c.client, - Logger: c.logger, - } +// RequeueAfter constructs a RequeueAfterError with the given duration +// setting. +func RequeueAfter(after time.Duration) error { + return RequeueAfterError(after) } -type mapperRequest struct{ res *pbresource.Resource } +// RequeueNow constructs a RequeueAfterError that reschedules the Request +// immediately. +func RequeueNow() error { + return RequeueAfterError(0) +} + +// Request represents a request to reconcile the resource with the given ID. +type Request struct { + // ID of the resource that needs to be reconciled. + ID *pbresource.ID +} // Key satisfies the queue.ItemType interface. It returns a string which will be // used to de-duplicate requests in the queue. -func (i mapperRequest) Key() string { +func (r Request) Key() string { return fmt.Sprintf( - "type=%q,part=%q,peer=%q,ns=%q,name=%q,uid=%q", - resource.ToGVK(i.res.Id.Type), - i.res.Id.Tenancy.Partition, - i.res.Id.Tenancy.PeerName, - i.res.Id.Tenancy.Namespace, - i.res.Id.Name, - i.res.Id.Uid, + "part=%q,peer=%q,ns=%q,name=%q,uid=%q", + r.ID.Tenancy.Partition, + r.ID.Tenancy.PeerName, + r.ID.Tenancy.Namespace, + r.ID.Name, + r.ID.Uid, ) } diff --git a/internal/controller/api_test.go b/internal/controller/controller_test.go similarity index 68% rename from internal/controller/api_test.go rename to internal/controller/controller_test.go index e6d2b8a034..db821c2511 100644 --- a/internal/controller/api_test.go +++ b/internal/controller/controller_test.go @@ -6,22 +6,47 @@ package controller_test import ( "context" "errors" + "fmt" "testing" "time" "github.com/stretchr/testify/require" svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing" - "github.com/hashicorp/consul/internal/controller" + controller "github.com/hashicorp/consul/internal/controller" + "github.com/hashicorp/consul/internal/controller/cache" + "github.com/hashicorp/consul/internal/controller/cache/index" + "github.com/hashicorp/consul/internal/controller/cache/indexers" + "github.com/hashicorp/consul/internal/controller/dependency" + "github.com/hashicorp/consul/internal/resource" "github.com/hashicorp/consul/internal/resource/demo" + "github.com/hashicorp/consul/internal/resource/resourcetest" "github.com/hashicorp/consul/proto-public/pbresource" + pbdemov1 "github.com/hashicorp/consul/proto/private/pbdemo/v1" + pbdemov2 "github.com/hashicorp/consul/proto/private/pbdemo/v2" "github.com/hashicorp/consul/proto/private/prototest" "github.com/hashicorp/consul/sdk/testutil" ) +var injectedError = errors.New("injected error") + +func errQuery(_ cache.ReadOnlyCache, _ ...any) (cache.ResourceIterator, error) { + return nil, injectedError +} + func TestController_API(t *testing.T) { t.Parallel() + idx := indexers.DecodedSingleIndexer("genre", index.SingleValueFromArgs(func(value string) ([]byte, error) { + var b index.Builder + b.String(value) + return b.Bytes(), nil + }), func(res *resource.DecodedResource[*pbdemov2.Artist]) (bool, []byte, error) { + var b index.Builder + b.String(res.Data.Genre.String()) + return true, b.Bytes(), nil + }) + rec := newTestReconciler() client := svctest.NewResourceServiceBuilder(). WithRegisterFns(demo.RegisterTypes). @@ -38,8 +63,9 @@ func TestController_API(t *testing.T) { } ctrl := controller. - ForType(demo.TypeV2Artist). - WithWatch(demo.TypeV2Album, controller.MapOwner). + NewController("artist", pbdemov2.ArtistType, idx). + WithWatch(pbdemov2.AlbumType, dependency.MapOwner, indexers.OwnerIndex("owner")). + WithQuery("some-query", errQuery). WithCustomWatch(concertSource, concertMapper). WithBackoff(10*time.Millisecond, 100*time.Millisecond). WithReconciler(rec) @@ -56,8 +82,21 @@ func TestController_API(t *testing.T) { rsp, err := client.Write(testContext(t), &pbresource.WriteRequest{Resource: res}) require.NoError(t, err) - req := rec.wait(t) + rt, req := rec.wait(t) prototest.AssertDeepEqual(t, rsp.Resource.Id, req.ID) + + // ensure that the cache index is being properly managed + dec := resourcetest.MustDecode[*pbdemov2.Artist](t, res) + resources, err := rt.Cache.List(pbdemov2.ArtistType, "genre", dec.Data.Genre.String()) + require.NoError(t, err) + prototest.AssertElementsMatch(t, []*pbresource.Resource{rsp.Resource}, resources) + + // ensure that the query was successfully registered - as we should not do equality + // checks on functions we are using a constant error return query to ensure it was + // registered properly. + iter, err := rt.Cache.Query("some-query", "irrelevant") + require.ErrorIs(t, err, injectedError) + require.Nil(t, iter) }) t.Run("watched resource type", func(t *testing.T) { @@ -67,7 +106,7 @@ func TestController_API(t *testing.T) { rsp, err := client.Write(testContext(t), &pbresource.WriteRequest{Resource: res}) require.NoError(t, err) - req := rec.wait(t) + _, req := rec.wait(t) prototest.AssertDeepEqual(t, rsp.Resource.Id, req.ID) rec.expectNoRequest(t, 500*time.Millisecond) @@ -75,11 +114,25 @@ func TestController_API(t *testing.T) { album, err := demo.GenerateV2Album(rsp.Resource.Id) require.NoError(t, err) - _, err = client.Write(testContext(t), &pbresource.WriteRequest{Resource: album}) + albumRsp1, err := client.Write(testContext(t), &pbresource.WriteRequest{Resource: album}) require.NoError(t, err) - req = rec.wait(t) + _, req = rec.wait(t) prototest.AssertDeepEqual(t, rsp.Resource.Id, req.ID) + + album2, err := demo.GenerateV2Album(rsp.Resource.Id) + require.NoError(t, err) + + albumRsp2, err := client.Write(testContext(t), &pbresource.WriteRequest{Resource: album2}) + require.NoError(t, err) + + rt, req := rec.wait(t) + prototest.AssertDeepEqual(t, rsp.Resource.Id, req.ID) + + // ensure that the watched type cache is being updated + resources, err := rt.Cache.List(pbdemov2.AlbumType, "owner", rsp.Resource.Id) + require.NoError(t, err) + prototest.AssertElementsMatch(t, []*pbresource.Resource{albumRsp1.Resource, albumRsp2.Resource}, resources) }) t.Run("custom watched resource type", func(t *testing.T) { @@ -89,14 +142,14 @@ func TestController_API(t *testing.T) { rsp, err := client.Write(testContext(t), &pbresource.WriteRequest{Resource: res}) require.NoError(t, err) - req := rec.wait(t) + _, req := rec.wait(t) prototest.AssertDeepEqual(t, rsp.Resource.Id, req.ID) rec.expectNoRequest(t, 500*time.Millisecond) concertsChan <- controller.Event{Obj: &Concert{name: "test-concert", artistID: rsp.Resource.Id}} - watchedReq := rec.wait(t) + _, watchedReq := rec.wait(t) prototest.AssertDeepEqual(t, req.ID, watchedReq.ID) otherArtist, err := demo.GenerateV2Artist() @@ -104,7 +157,7 @@ func TestController_API(t *testing.T) { concertsChan <- controller.Event{Obj: &Concert{name: "test-concert", artistID: otherArtist.Id}} - watchedReq = rec.wait(t) + _, watchedReq = rec.wait(t) prototest.AssertDeepEqual(t, otherArtist.Id, watchedReq.ID) }) @@ -117,11 +170,11 @@ func TestController_API(t *testing.T) { rsp, err := client.Write(testContext(t), &pbresource.WriteRequest{Resource: res}) require.NoError(t, err) - req := rec.wait(t) + _, req := rec.wait(t) prototest.AssertDeepEqual(t, rsp.Resource.Id, req.ID) // Reconciler should be called with the same request again. - req = rec.wait(t) + _, req = rec.wait(t) prototest.AssertDeepEqual(t, rsp.Resource.Id, req.ID) }) @@ -134,11 +187,11 @@ func TestController_API(t *testing.T) { rsp, err := client.Write(testContext(t), &pbresource.WriteRequest{Resource: res}) require.NoError(t, err) - req := rec.wait(t) + _, req := rec.wait(t) prototest.AssertDeepEqual(t, rsp.Resource.Id, req.ID) // Reconciler should be called with the same request again. - req = rec.wait(t) + _, req = rec.wait(t) prototest.AssertDeepEqual(t, rsp.Resource.Id, req.ID) }) @@ -151,12 +204,12 @@ func TestController_API(t *testing.T) { rsp, err := client.Write(testContext(t), &pbresource.WriteRequest{Resource: res}) require.NoError(t, err) - req := rec.wait(t) + _, req := rec.wait(t) prototest.AssertDeepEqual(t, rsp.Resource.Id, req.ID) rec.expectNoRequest(t, 750*time.Millisecond) - req = rec.wait(t) + _, req = rec.wait(t) prototest.AssertDeepEqual(t, rsp.Resource.Id, req.ID) }) } @@ -171,8 +224,8 @@ func TestController_Placement(t *testing.T) { Run(t) ctrl := controller. - ForType(demo.TypeV2Artist). - WithWatch(demo.TypeV2Album, controller.MapOwner). + NewController("artist", pbdemov2.ArtistType). + WithWatch(pbdemov2.AlbumType, dependency.MapOwner). WithPlacement(controller.PlacementSingleton). WithReconciler(rec) @@ -190,7 +243,7 @@ func TestController_Placement(t *testing.T) { // Become the leader and check the reconciler is called. mgr.SetRaftLeader(true) - _ = rec.wait(t) + _, _ = rec.wait(t) // Should not be called after losing leadership. mgr.SetRaftLeader(false) @@ -206,8 +259,8 @@ func TestController_Placement(t *testing.T) { Run(t) ctrl := controller. - ForType(demo.TypeV2Artist). - WithWatch(demo.TypeV2Album, controller.MapOwner). + NewController("artist", pbdemov2.ArtistType). + WithWatch(pbdemov2.AlbumType, dependency.MapOwner). WithPlacement(controller.PlacementEachServer). WithReconciler(rec) @@ -221,19 +274,19 @@ func TestController_Placement(t *testing.T) { // Reconciler should be called even though we're not the Raft leader. _, err = client.Write(testContext(t), &pbresource.WriteRequest{Resource: res}) require.NoError(t, err) - _ = rec.wait(t) + _, _ = rec.wait(t) }) } func TestController_String(t *testing.T) { ctrl := controller. - ForType(demo.TypeV2Artist). - WithWatch(demo.TypeV2Album, controller.MapOwner). + NewController("artist", pbdemov2.ArtistType). + WithWatch(pbdemov2.AlbumType, dependency.MapOwner). WithBackoff(5*time.Second, 1*time.Hour). WithPlacement(controller.PlacementEachServer) require.Equal(t, - `, placement="each-server">`, + `, placement=each-server>`, ctrl.String(), ) } @@ -245,9 +298,9 @@ func TestController_NoReconciler(t *testing.T) { mgr := controller.NewManager(client, testutil.Logger(t)) - ctrl := controller.ForType(demo.TypeV2Artist) + ctrl := controller.NewController("artist", pbdemov2.ArtistType) require.PanicsWithValue(t, - `cannot register controller without a reconciler , placement="singleton">`, + fmt.Sprintf("cannot register controller without a reconciler %s", ctrl.String()), func() { mgr.Register(ctrl) }) } @@ -262,7 +315,7 @@ func TestController_Watch(t *testing.T) { Run(t) ctrl := controller. - ForType(demo.TypeV1RecordLabel). + NewController("labels", pbdemov1.RecordLabelType). WithReconciler(rec) mgr := controller.NewManager(client, testutil.Logger(t)) @@ -278,7 +331,7 @@ func TestController_Watch(t *testing.T) { rsp, err := client.Write(testContext(t), &pbresource.WriteRequest{Resource: res}) require.NoError(t, err) - req := rec.wait(t) + _, req := rec.wait(t) prototest.AssertDeepEqual(t, rsp.Resource.Id, req.ID) }) @@ -290,7 +343,7 @@ func TestController_Watch(t *testing.T) { Run(t) ctrl := controller. - ForType(demo.TypeV1Executive). + NewController("executives", pbdemov1.ExecutiveType). WithReconciler(rec) mgr := controller.NewManager(client, testutil.Logger(t)) @@ -305,7 +358,7 @@ func TestController_Watch(t *testing.T) { rsp, err := client.Write(testContext(t), &pbresource.WriteRequest{Resource: exec}) require.NoError(t, err) - req := rec.wait(t) + _, req := rec.wait(t) prototest.AssertDeepEqual(t, rsp.Resource.Id, req.ID) }) @@ -317,7 +370,7 @@ func TestController_Watch(t *testing.T) { Run(t) ctrl := controller. - ForType(demo.TypeV2Artist). + NewController("artists", pbdemov2.ArtistType). WithReconciler(rec) mgr := controller.NewManager(client, testutil.Logger(t)) @@ -332,27 +385,32 @@ func TestController_Watch(t *testing.T) { rsp, err := client.Write(testContext(t), &pbresource.WriteRequest{Resource: artist}) require.NoError(t, err) - req := rec.wait(t) + _, req := rec.wait(t) prototest.AssertDeepEqual(t, rsp.Resource.Id, req.ID) }) } func newTestReconciler() *testReconciler { return &testReconciler{ - calls: make(chan controller.Request), + calls: make(chan requestArgs), errors: make(chan error, 1), panics: make(chan any, 1), } } +type requestArgs struct { + req controller.Request + rt controller.Runtime +} + type testReconciler struct { - calls chan controller.Request + calls chan requestArgs errors chan error panics chan any } -func (r *testReconciler) Reconcile(_ context.Context, _ controller.Runtime, req controller.Request) error { - r.calls <- req +func (r *testReconciler) Reconcile(_ context.Context, rt controller.Runtime, req controller.Request) error { + r.calls <- requestArgs{req: req, rt: rt} select { case err := <-r.errors: @@ -372,22 +430,22 @@ func (r *testReconciler) expectNoRequest(t *testing.T, duration time.Duration) { started := time.Now() select { - case req := <-r.calls: - t.Fatalf("expected no request for %s, but got: %s after %s", duration, req.ID, time.Since(started)) + case args := <-r.calls: + t.Fatalf("expected no request for %s, but got: %s after %s", duration, args.req.ID, time.Since(started)) case <-time.After(duration): } } -func (r *testReconciler) wait(t *testing.T) controller.Request { +func (r *testReconciler) wait(t *testing.T) (controller.Runtime, controller.Request) { t.Helper() - var req controller.Request + var args requestArgs select { - case req = <-r.calls: + case args = <-r.calls: case <-time.After(500 * time.Millisecond): t.Fatal("Reconcile was not called after 500ms") } - return req + return args.rt, args.req } func testContext(t *testing.T) context.Context { diff --git a/internal/controller/controllermock/mock_CacheIDModifier.go b/internal/controller/controllermock/mock_CacheIDModifier.go new file mode 100644 index 0000000000..3b86dc0997 --- /dev/null +++ b/internal/controller/controllermock/mock_CacheIDModifier.go @@ -0,0 +1,95 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package controllermock + +import ( + context "context" + + controller "github.com/hashicorp/consul/internal/controller" + mock "github.com/stretchr/testify/mock" + + pbresource "github.com/hashicorp/consul/proto-public/pbresource" +) + +// CacheIDModifier is an autogenerated mock type for the CacheIDModifier type +type CacheIDModifier struct { + mock.Mock +} + +type CacheIDModifier_Expecter struct { + mock *mock.Mock +} + +func (_m *CacheIDModifier) EXPECT() *CacheIDModifier_Expecter { + return &CacheIDModifier_Expecter{mock: &_m.Mock} +} + +// Execute provides a mock function with given fields: ctx, rt, id +func (_m *CacheIDModifier) Execute(ctx context.Context, rt controller.Runtime, id *pbresource.ID) (*pbresource.ID, error) { + ret := _m.Called(ctx, rt, id) + + var r0 *pbresource.ID + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, controller.Runtime, *pbresource.ID) (*pbresource.ID, error)); ok { + return rf(ctx, rt, id) + } + if rf, ok := ret.Get(0).(func(context.Context, controller.Runtime, *pbresource.ID) *pbresource.ID); ok { + r0 = rf(ctx, rt, id) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbresource.ID) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, controller.Runtime, *pbresource.ID) error); ok { + r1 = rf(ctx, rt, id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// CacheIDModifier_Execute_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Execute' +type CacheIDModifier_Execute_Call struct { + *mock.Call +} + +// Execute is a helper method to define mock.On call +// - ctx context.Context +// - rt controller.Runtime +// - id *pbresource.ID +func (_e *CacheIDModifier_Expecter) Execute(ctx interface{}, rt interface{}, id interface{}) *CacheIDModifier_Execute_Call { + return &CacheIDModifier_Execute_Call{Call: _e.mock.On("Execute", ctx, rt, id)} +} + +func (_c *CacheIDModifier_Execute_Call) Run(run func(ctx context.Context, rt controller.Runtime, id *pbresource.ID)) *CacheIDModifier_Execute_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(controller.Runtime), args[2].(*pbresource.ID)) + }) + return _c +} + +func (_c *CacheIDModifier_Execute_Call) Return(_a0 *pbresource.ID, _a1 error) *CacheIDModifier_Execute_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *CacheIDModifier_Execute_Call) RunAndReturn(run func(context.Context, controller.Runtime, *pbresource.ID) (*pbresource.ID, error)) *CacheIDModifier_Execute_Call { + _c.Call.Return(run) + return _c +} + +// NewCacheIDModifier creates a new instance of CacheIDModifier. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewCacheIDModifier(t interface { + mock.TestingT + Cleanup(func()) +}) *CacheIDModifier { + mock := &CacheIDModifier{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/controller/controllermock/mock_CustomDependencyMapper.go b/internal/controller/controllermock/mock_CustomDependencyMapper.go new file mode 100644 index 0000000000..70e25ca98f --- /dev/null +++ b/internal/controller/controllermock/mock_CustomDependencyMapper.go @@ -0,0 +1,93 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package controllermock + +import ( + context "context" + + controller "github.com/hashicorp/consul/internal/controller" + mock "github.com/stretchr/testify/mock" +) + +// CustomDependencyMapper is an autogenerated mock type for the CustomDependencyMapper type +type CustomDependencyMapper struct { + mock.Mock +} + +type CustomDependencyMapper_Expecter struct { + mock *mock.Mock +} + +func (_m *CustomDependencyMapper) EXPECT() *CustomDependencyMapper_Expecter { + return &CustomDependencyMapper_Expecter{mock: &_m.Mock} +} + +// Execute provides a mock function with given fields: ctx, rt, event +func (_m *CustomDependencyMapper) Execute(ctx context.Context, rt controller.Runtime, event controller.Event) ([]controller.Request, error) { + ret := _m.Called(ctx, rt, event) + + var r0 []controller.Request + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, controller.Runtime, controller.Event) ([]controller.Request, error)); ok { + return rf(ctx, rt, event) + } + if rf, ok := ret.Get(0).(func(context.Context, controller.Runtime, controller.Event) []controller.Request); ok { + r0 = rf(ctx, rt, event) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]controller.Request) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, controller.Runtime, controller.Event) error); ok { + r1 = rf(ctx, rt, event) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// CustomDependencyMapper_Execute_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Execute' +type CustomDependencyMapper_Execute_Call struct { + *mock.Call +} + +// Execute is a helper method to define mock.On call +// - ctx context.Context +// - rt controller.Runtime +// - event controller.Event +func (_e *CustomDependencyMapper_Expecter) Execute(ctx interface{}, rt interface{}, event interface{}) *CustomDependencyMapper_Execute_Call { + return &CustomDependencyMapper_Execute_Call{Call: _e.mock.On("Execute", ctx, rt, event)} +} + +func (_c *CustomDependencyMapper_Execute_Call) Run(run func(ctx context.Context, rt controller.Runtime, event controller.Event)) *CustomDependencyMapper_Execute_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(controller.Runtime), args[2].(controller.Event)) + }) + return _c +} + +func (_c *CustomDependencyMapper_Execute_Call) Return(_a0 []controller.Request, _a1 error) *CustomDependencyMapper_Execute_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *CustomDependencyMapper_Execute_Call) RunAndReturn(run func(context.Context, controller.Runtime, controller.Event) ([]controller.Request, error)) *CustomDependencyMapper_Execute_Call { + _c.Call.Return(run) + return _c +} + +// NewCustomDependencyMapper creates a new instance of CustomDependencyMapper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewCustomDependencyMapper(t interface { + mock.TestingT + Cleanup(func()) +}) *CustomDependencyMapper { + mock := &CustomDependencyMapper{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/controller/controllermock/mock_DependencyMapper.go b/internal/controller/controllermock/mock_DependencyMapper.go new file mode 100644 index 0000000000..be2519783a --- /dev/null +++ b/internal/controller/controllermock/mock_DependencyMapper.go @@ -0,0 +1,95 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package controllermock + +import ( + context "context" + + controller "github.com/hashicorp/consul/internal/controller" + mock "github.com/stretchr/testify/mock" + + pbresource "github.com/hashicorp/consul/proto-public/pbresource" +) + +// DependencyMapper is an autogenerated mock type for the DependencyMapper type +type DependencyMapper struct { + mock.Mock +} + +type DependencyMapper_Expecter struct { + mock *mock.Mock +} + +func (_m *DependencyMapper) EXPECT() *DependencyMapper_Expecter { + return &DependencyMapper_Expecter{mock: &_m.Mock} +} + +// Execute provides a mock function with given fields: ctx, rt, res +func (_m *DependencyMapper) Execute(ctx context.Context, rt controller.Runtime, res *pbresource.Resource) ([]controller.Request, error) { + ret := _m.Called(ctx, rt, res) + + var r0 []controller.Request + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, controller.Runtime, *pbresource.Resource) ([]controller.Request, error)); ok { + return rf(ctx, rt, res) + } + if rf, ok := ret.Get(0).(func(context.Context, controller.Runtime, *pbresource.Resource) []controller.Request); ok { + r0 = rf(ctx, rt, res) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]controller.Request) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, controller.Runtime, *pbresource.Resource) error); ok { + r1 = rf(ctx, rt, res) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// DependencyMapper_Execute_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Execute' +type DependencyMapper_Execute_Call struct { + *mock.Call +} + +// Execute is a helper method to define mock.On call +// - ctx context.Context +// - rt controller.Runtime +// - res *pbresource.Resource +func (_e *DependencyMapper_Expecter) Execute(ctx interface{}, rt interface{}, res interface{}) *DependencyMapper_Execute_Call { + return &DependencyMapper_Execute_Call{Call: _e.mock.On("Execute", ctx, rt, res)} +} + +func (_c *DependencyMapper_Execute_Call) Run(run func(ctx context.Context, rt controller.Runtime, res *pbresource.Resource)) *DependencyMapper_Execute_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(controller.Runtime), args[2].(*pbresource.Resource)) + }) + return _c +} + +func (_c *DependencyMapper_Execute_Call) Return(_a0 []controller.Request, _a1 error) *DependencyMapper_Execute_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *DependencyMapper_Execute_Call) RunAndReturn(run func(context.Context, controller.Runtime, *pbresource.Resource) ([]controller.Request, error)) *DependencyMapper_Execute_Call { + _c.Call.Return(run) + return _c +} + +// NewDependencyMapper creates a new instance of DependencyMapper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewDependencyMapper(t interface { + mock.TestingT + Cleanup(func()) +}) *DependencyMapper { + mock := &DependencyMapper{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/controller/controllermock/mock_DependencyTransform.go b/internal/controller/controllermock/mock_DependencyTransform.go new file mode 100644 index 0000000000..7f1611c673 --- /dev/null +++ b/internal/controller/controllermock/mock_DependencyTransform.go @@ -0,0 +1,95 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package controllermock + +import ( + context "context" + + controller "github.com/hashicorp/consul/internal/controller" + mock "github.com/stretchr/testify/mock" + + pbresource "github.com/hashicorp/consul/proto-public/pbresource" +) + +// DependencyTransform is an autogenerated mock type for the DependencyTransform type +type DependencyTransform struct { + mock.Mock +} + +type DependencyTransform_Expecter struct { + mock *mock.Mock +} + +func (_m *DependencyTransform) EXPECT() *DependencyTransform_Expecter { + return &DependencyTransform_Expecter{mock: &_m.Mock} +} + +// Execute provides a mock function with given fields: ctx, rt, res +func (_m *DependencyTransform) Execute(ctx context.Context, rt controller.Runtime, res *pbresource.Resource) ([]*pbresource.Resource, error) { + ret := _m.Called(ctx, rt, res) + + var r0 []*pbresource.Resource + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, controller.Runtime, *pbresource.Resource) ([]*pbresource.Resource, error)); ok { + return rf(ctx, rt, res) + } + if rf, ok := ret.Get(0).(func(context.Context, controller.Runtime, *pbresource.Resource) []*pbresource.Resource); ok { + r0 = rf(ctx, rt, res) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*pbresource.Resource) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, controller.Runtime, *pbresource.Resource) error); ok { + r1 = rf(ctx, rt, res) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// DependencyTransform_Execute_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Execute' +type DependencyTransform_Execute_Call struct { + *mock.Call +} + +// Execute is a helper method to define mock.On call +// - ctx context.Context +// - rt controller.Runtime +// - res *pbresource.Resource +func (_e *DependencyTransform_Expecter) Execute(ctx interface{}, rt interface{}, res interface{}) *DependencyTransform_Execute_Call { + return &DependencyTransform_Execute_Call{Call: _e.mock.On("Execute", ctx, rt, res)} +} + +func (_c *DependencyTransform_Execute_Call) Run(run func(ctx context.Context, rt controller.Runtime, res *pbresource.Resource)) *DependencyTransform_Execute_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(controller.Runtime), args[2].(*pbresource.Resource)) + }) + return _c +} + +func (_c *DependencyTransform_Execute_Call) Return(_a0 []*pbresource.Resource, _a1 error) *DependencyTransform_Execute_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *DependencyTransform_Execute_Call) RunAndReturn(run func(context.Context, controller.Runtime, *pbresource.Resource) ([]*pbresource.Resource, error)) *DependencyTransform_Execute_Call { + _c.Call.Return(run) + return _c +} + +// NewDependencyTransform creates a new instance of DependencyTransform. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewDependencyTransform(t interface { + mock.TestingT + Cleanup(func()) +}) *DependencyTransform { + mock := &DependencyTransform{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/controller/controllermock/mock_Lease.go b/internal/controller/controllermock/mock_Lease.go new file mode 100644 index 0000000000..eb9e5cda77 --- /dev/null +++ b/internal/controller/controllermock/mock_Lease.go @@ -0,0 +1,116 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package controllermock + +import mock "github.com/stretchr/testify/mock" + +// Lease is an autogenerated mock type for the Lease type +type Lease struct { + mock.Mock +} + +type Lease_Expecter struct { + mock *mock.Mock +} + +func (_m *Lease) EXPECT() *Lease_Expecter { + return &Lease_Expecter{mock: &_m.Mock} +} + +// Changed provides a mock function with given fields: +func (_m *Lease) Changed() <-chan struct{} { + ret := _m.Called() + + var r0 <-chan struct{} + if rf, ok := ret.Get(0).(func() <-chan struct{}); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(<-chan struct{}) + } + } + + return r0 +} + +// Lease_Changed_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Changed' +type Lease_Changed_Call struct { + *mock.Call +} + +// Changed is a helper method to define mock.On call +func (_e *Lease_Expecter) Changed() *Lease_Changed_Call { + return &Lease_Changed_Call{Call: _e.mock.On("Changed")} +} + +func (_c *Lease_Changed_Call) Run(run func()) *Lease_Changed_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *Lease_Changed_Call) Return(_a0 <-chan struct{}) *Lease_Changed_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *Lease_Changed_Call) RunAndReturn(run func() <-chan struct{}) *Lease_Changed_Call { + _c.Call.Return(run) + return _c +} + +// Held provides a mock function with given fields: +func (_m *Lease) Held() bool { + ret := _m.Called() + + var r0 bool + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// Lease_Held_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Held' +type Lease_Held_Call struct { + *mock.Call +} + +// Held is a helper method to define mock.On call +func (_e *Lease_Expecter) Held() *Lease_Held_Call { + return &Lease_Held_Call{Call: _e.mock.On("Held")} +} + +func (_c *Lease_Held_Call) Run(run func()) *Lease_Held_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *Lease_Held_Call) Return(_a0 bool) *Lease_Held_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *Lease_Held_Call) RunAndReturn(run func() bool) *Lease_Held_Call { + _c.Call.Return(run) + return _c +} + +// NewLease creates a new instance of Lease. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewLease(t interface { + mock.TestingT + Cleanup(func()) +}) *Lease { + mock := &Lease{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/controller/controllermock/mock_Reconciler.go b/internal/controller/controllermock/mock_Reconciler.go new file mode 100644 index 0000000000..5488978ea4 --- /dev/null +++ b/internal/controller/controllermock/mock_Reconciler.go @@ -0,0 +1,81 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package controllermock + +import ( + context "context" + + controller "github.com/hashicorp/consul/internal/controller" + mock "github.com/stretchr/testify/mock" +) + +// Reconciler is an autogenerated mock type for the Reconciler type +type Reconciler struct { + mock.Mock +} + +type Reconciler_Expecter struct { + mock *mock.Mock +} + +func (_m *Reconciler) EXPECT() *Reconciler_Expecter { + return &Reconciler_Expecter{mock: &_m.Mock} +} + +// Reconcile provides a mock function with given fields: ctx, rt, req +func (_m *Reconciler) Reconcile(ctx context.Context, rt controller.Runtime, req controller.Request) error { + ret := _m.Called(ctx, rt, req) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, controller.Runtime, controller.Request) error); ok { + r0 = rf(ctx, rt, req) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Reconciler_Reconcile_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Reconcile' +type Reconciler_Reconcile_Call struct { + *mock.Call +} + +// Reconcile is a helper method to define mock.On call +// - ctx context.Context +// - rt controller.Runtime +// - req controller.Request +func (_e *Reconciler_Expecter) Reconcile(ctx interface{}, rt interface{}, req interface{}) *Reconciler_Reconcile_Call { + return &Reconciler_Reconcile_Call{Call: _e.mock.On("Reconcile", ctx, rt, req)} +} + +func (_c *Reconciler_Reconcile_Call) Run(run func(ctx context.Context, rt controller.Runtime, req controller.Request)) *Reconciler_Reconcile_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(controller.Runtime), args[2].(controller.Request)) + }) + return _c +} + +func (_c *Reconciler_Reconcile_Call) Return(_a0 error) *Reconciler_Reconcile_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *Reconciler_Reconcile_Call) RunAndReturn(run func(context.Context, controller.Runtime, controller.Request) error) *Reconciler_Reconcile_Call { + _c.Call.Return(run) + return _c +} + +// NewReconciler creates a new instance of Reconciler. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewReconciler(t interface { + mock.TestingT + Cleanup(func()) +}) *Reconciler { + mock := &Reconciler{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/controller/controllermock/mock_task.go b/internal/controller/controllermock/mock_task.go new file mode 100644 index 0000000000..6707fc1e56 --- /dev/null +++ b/internal/controller/controllermock/mock_task.go @@ -0,0 +1,78 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package controllermock + +import ( + context "context" + + mock "github.com/stretchr/testify/mock" +) + +// task is an autogenerated mock type for the task type +type task struct { + mock.Mock +} + +type task_Expecter struct { + mock *mock.Mock +} + +func (_m *task) EXPECT() *task_Expecter { + return &task_Expecter{mock: &_m.Mock} +} + +// Execute provides a mock function with given fields: _a0 +func (_m *task) Execute(_a0 context.Context) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// task_Execute_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Execute' +type task_Execute_Call struct { + *mock.Call +} + +// Execute is a helper method to define mock.On call +// - _a0 context.Context +func (_e *task_Expecter) Execute(_a0 interface{}) *task_Execute_Call { + return &task_Execute_Call{Call: _e.mock.On("Execute", _a0)} +} + +func (_c *task_Execute_Call) Run(run func(_a0 context.Context)) *task_Execute_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *task_Execute_Call) Return(_a0 error) *task_Execute_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *task_Execute_Call) RunAndReturn(run func(context.Context) error) *task_Execute_Call { + _c.Call.Return(run) + return _c +} + +// newTask creates a new instance of task. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func newTask(t interface { + mock.TestingT + Cleanup(func()) +}) *task { + mock := &task{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/controller/custom_watch.go b/internal/controller/custom_watch.go new file mode 100644 index 0000000000..627a5e48f4 --- /dev/null +++ b/internal/controller/custom_watch.go @@ -0,0 +1,57 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package controller + +import ( + "context" + + "github.com/hashicorp/consul/agent/consul/controller/queue" +) + +// CustomDependencyMapper is called when an Event occurs to determine which of the +// controller's managed resources need to be reconciled. +type CustomDependencyMapper func( + ctx context.Context, + rt Runtime, + event Event, +) ([]Request, error) + +// Watch is responsible for watching for custom events from source and adding them to +// the event queue. +func (s *Source) Watch(ctx context.Context, add func(e Event)) error { + for { + select { + case <-ctx.Done(): + return nil + case evt, ok := <-s.Source: + if !ok { + return nil + } + add(evt) + } + } +} + +// Source is used as a generic source of events. This can be used when events aren't coming from resources +// stored by the resource API. +type Source struct { + Source <-chan Event +} + +// Event captures an event in the system which the API can choose to respond to. +type Event struct { + Obj queue.ItemType +} + +// Key returns a string that will be used to de-duplicate items in the queue. +func (e Event) Key() string { + return e.Obj.Key() +} + +// customWatch represent a Watch on a custom Event source and a Mapper to map said +// Events into Requests that the controller can respond to. +type customWatch struct { + source *Source + mapper CustomDependencyMapper +} diff --git a/internal/controller/dependencies.go b/internal/controller/dependencies.go index 6a91d91ff7..a7ae0e96d6 100644 --- a/internal/controller/dependencies.go +++ b/internal/controller/dependencies.go @@ -71,7 +71,7 @@ func (m *Manager) CalculateDependencies(registrations []resource.Registration) D watches = append(watches, typeToString(w.watchedType)) } - out[typeToString(c.managedType)] = watches + out[typeToString(c.managedTypeWatch.watchedType)] = watches } return out diff --git a/internal/controller/dependency/.mockery.yaml b/internal/controller/dependency/.mockery.yaml new file mode 100644 index 0000000000..4595f3b527 --- /dev/null +++ b/internal/controller/dependency/.mockery.yaml @@ -0,0 +1,11 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +with-expecter: true +all: true +recursive: false +dir: "{{.PackageName}}mock" +outpkg: "{{.PackageName}}mock" +mockname: "{{.InterfaceName}}" +packages: + github.com/hashicorp/consul/internal/controller/dependency: diff --git a/internal/controller/dependency/cache..go b/internal/controller/dependency/cache..go new file mode 100644 index 0000000000..321e57ac6d --- /dev/null +++ b/internal/controller/dependency/cache..go @@ -0,0 +1,218 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package dependency + +import ( + "context" + "fmt" + + "github.com/hashicorp/consul/internal/controller" + "github.com/hashicorp/consul/internal/resource" + "github.com/hashicorp/consul/proto-public/pbresource" +) + +// CacheIDModifier is used to alter the Resource ID of the various Cache* +// controller.DependencyMappers prior to making the request to the cache. This is most +// useful to replace the type of resource queried (such as when resource types +// are name aligned) or to modify the tenancy in some way. +type CacheIDModifier func( + ctx context.Context, + rt controller.Runtime, + id *pbresource.ID, +) (*pbresource.ID, error) + +// CacheGetMapper is used to map an event for a watched resource by performing a Get of +// a single cached resource of any type. +func CacheGetMapper(indexedType *pbresource.Type, indexName string, mods ...CacheIDModifier) controller.DependencyMapper { + return func(ctx context.Context, rt controller.Runtime, res *pbresource.Resource) ([]controller.Request, error) { + id, err := applyCacheIDMods(ctx, rt, res.GetId(), mods...) + if err != nil { + return nil, err + } + + if rt.Logger.IsTrace() { + rt.Logger.Trace("mapping dependencies from cache", + "type", resource.ToGVK(indexedType), + "index", indexName, + "resource", resource.IDToString(res.GetId()), + ) + } + mapped, err := rt.Cache.Get(indexedType, indexName, id) + if err != nil { + rt.Logger.Error("failed to map dependencies from the cache", + "type", resource.ToGVK(indexedType), + "index", indexName, + "resource", resource.IDToString(res.GetId()), + "error", err, + ) + return nil, fmt.Errorf("failed to list from cache index %q on type %q: %w", indexName, resource.ToGVK(indexedType), err) + } + + results := []controller.Request{ + {ID: mapped.GetId()}, + } + if rt.Logger.IsTrace() { + rt.Logger.Trace("mapped dependencies", + "type", resource.ToGVK(indexedType), + "index", indexName, + "resource", resource.IDToString(res.GetId()), + "dependency", mapped.GetId(), + ) + } + + return results, nil + } +} + +// CacheListMapper is used to map the incoming resource to a set of requests for all +// the cached resources returned by the caches List operation. +func CacheListMapper(indexedType *pbresource.Type, indexName string, mods ...CacheIDModifier) controller.DependencyMapper { + return func(ctx context.Context, rt controller.Runtime, res *pbresource.Resource) ([]controller.Request, error) { + id, err := applyCacheIDMods(ctx, rt, res.GetId(), mods...) + if err != nil { + return nil, err + } + + if rt.Logger.IsTrace() { + rt.Logger.Trace("mapping dependencies from cache", + "type", resource.ToGVK(indexedType), + "index", indexName, + "resource", resource.IDToString(res.GetId()), + ) + } + iter, err := rt.Cache.ListIterator(indexedType, indexName, id) + if err != nil { + rt.Logger.Error("failed to map dependencies from the cache", + "type", resource.ToGVK(indexedType), + "index", indexName, + "resource", resource.IDToString(res.GetId()), + "error", err, + ) + return nil, fmt.Errorf("failed to list from cache index %q on type %q: %w", indexName, resource.ToGVK(indexedType), err) + } + + var results []controller.Request + for res := iter.Next(); res != nil; res = iter.Next() { + results = append(results, controller.Request{ID: res.GetId()}) + } + if rt.Logger.IsTrace() { + rt.Logger.Trace("mapped dependencies", + "type", resource.ToGVK(indexedType), + "index", indexName, + "resource", resource.IDToString(res.GetId()), + "dependencies", results, + ) + } + + return results, nil + } +} + +// CacheParentsMapper is used to map the incoming resource to a set of requests for all +// the cached resources returned by the caches Parents operation. +func CacheParentsMapper(indexedType *pbresource.Type, indexName string, mods ...CacheIDModifier) controller.DependencyMapper { + return func(ctx context.Context, rt controller.Runtime, res *pbresource.Resource) ([]controller.Request, error) { + id, err := applyCacheIDMods(ctx, rt, res.GetId(), mods...) + if err != nil { + return nil, err + } + + if rt.Logger.IsTrace() { + rt.Logger.Trace("mapping dependencies from cache", + "type", resource.ToGVK(indexedType), + "index", indexName, + "resource", resource.IDToString(res.GetId()), + ) + } + iter, err := rt.Cache.ParentsIterator(indexedType, indexName, id) + if err != nil { + rt.Logger.Error("failed to map dependencies from the cache", + "type", resource.ToGVK(indexedType), + "index", indexName, + "resource", resource.IDToString(res.GetId()), + "error", err, + ) + return nil, fmt.Errorf("failed to list from cache index %q on type %q: %w", indexName, resource.ToGVK(indexedType), err) + } + + var results []controller.Request + for res := iter.Next(); res != nil; res = iter.Next() { + results = append(results, controller.Request{ID: res.GetId()}) + } + if rt.Logger.IsTrace() { + rt.Logger.Trace("mapped dependencies", + "type", resource.ToGVK(indexedType), + "index", indexName, + "resource", resource.IDToString(res.GetId()), + "dependencies", results, + ) + } + + return results, nil + } +} + +// CacheListTransform operates like CacheListMapper. The only difference is that the cache results +// are left as the whole cached resource instead of condensing down to just their IDs. +func CacheListTransform(indexedType *pbresource.Type, indexName string, mods ...CacheIDModifier) DependencyTransform { + return func(ctx context.Context, rt controller.Runtime, res *pbresource.Resource) ([]*pbresource.Resource, error) { + id, err := applyCacheIDMods(ctx, rt, res.GetId(), mods...) + if err != nil { + return nil, err + } + + if rt.Logger.IsTrace() { + rt.Logger.Trace("transforming dependencies from cache", + "type", resource.ToGVK(indexedType), + "index", indexName, + "resource", resource.IDToString(res.GetId()), + ) + } + results, err := rt.Cache.List(indexedType, indexName, id) + if err != nil { + rt.Logger.Error("failed to transform dependencies from the cache", + "type", resource.ToGVK(indexedType), + "index", indexName, + "resource", resource.IDToString(res.GetId()), + "error", err, + ) + return nil, fmt.Errorf( + "failed to list from cache index %q on type %q: %w", + indexName, + resource.ToGVK(indexedType), + err, + ) + } + + if rt.Logger.IsTrace() { + rt.Logger.Trace("transformed dependencies", + "type", resource.ToGVK(indexedType), + "index", indexName, + "resource", resource.IDToString(res.GetId()), + "dependencies", results, + ) + } + + return results, nil + } +} + +// ReplaceCacheIDType will generate a CacheIDModifier that replaces the original ID's +// type with the desired type specified. +func ReplaceCacheIDType(desiredType *pbresource.Type) CacheIDModifier { + return func(_ context.Context, _ controller.Runtime, id *pbresource.ID) (*pbresource.ID, error) { + return resource.ReplaceType(desiredType, id), nil + } +} + +func applyCacheIDMods(ctx context.Context, rt controller.Runtime, id *pbresource.ID, mods ...CacheIDModifier) (*pbresource.ID, error) { + var err error + for _, mod := range mods { + id, err = mod(ctx, rt, id) + if err != nil { + return nil, err + } + } + return id, err +} diff --git a/internal/controller/dependency/cache_test.go b/internal/controller/dependency/cache_test.go new file mode 100644 index 0000000000..8b686e22e4 --- /dev/null +++ b/internal/controller/dependency/cache_test.go @@ -0,0 +1,346 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package dependency + +import ( + "context" + "testing" + + "github.com/hashicorp/consul/internal/controller" + "github.com/hashicorp/consul/internal/controller/cache/cachemock" + "github.com/hashicorp/consul/internal/controller/dependency/dependencymock" + "github.com/hashicorp/consul/internal/resource" + "github.com/hashicorp/consul/internal/resource/resourcetest" + "github.com/hashicorp/consul/proto-public/pbresource" + "github.com/hashicorp/consul/proto/private/prototest" + "github.com/hashicorp/consul/sdk/testutil" + "github.com/hashicorp/go-hclog" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" +) + +type cacheSuite struct { + suite.Suite + + cache *cachemock.ReadOnlyCache + idMod *dependencymock.CacheIDModifier + res *pbresource.Resource + rt controller.Runtime +} + +func (suite *cacheSuite) SetupTest() { + suite.res = resourcetest.Resource(fakeMapType, "foo"). + WithTenancy(resource.DefaultNamespacedTenancy()). + Build() + suite.idMod = dependencymock.NewCacheIDModifier(suite.T()) + suite.cache = cachemock.NewReadOnlyCache(suite.T()) + suite.rt = controller.Runtime{ + Cache: suite.cache, + Logger: testutil.Logger(suite.T()), + } +} + +func (suite *cacheSuite) TestGetMapper_ModErr() { + suite.idMod.EXPECT(). + Execute(mock.Anything, suite.rt, suite.res.Id). + Return(nil, injectedErr). + Once() + + reqs, err := CacheGetMapper(suite.res.Id.Type, "doesnt-matter", suite.idMod.Execute)( + context.Background(), + suite.rt, + suite.res, + ) + + require.Nil(suite.T(), reqs) + require.ErrorIs(suite.T(), err, injectedErr) +} + +func (suite *cacheSuite) TestGetMapper_CacheErr() { + id := resourceID("testing", "v1", "fake", "foo") + suite.idMod.EXPECT(). + Execute(mock.Anything, suite.rt, suite.res.Id). + Return(id, nil). + Once() + + suite.cache.EXPECT(). + Get(suite.res.Id.Type, "fake-index", id). + Return(nil, injectedErr). + Once() + + reqs, err := CacheGetMapper(suite.res.Id.Type, "fake-index", suite.idMod.Execute)( + context.Background(), + suite.rt, + suite.res, + ) + + require.Nil(suite.T(), reqs) + require.ErrorIs(suite.T(), err, injectedErr) +} + +func (suite *cacheSuite) TestGetMapper_Ok() { + out := resourcetest.Resource(altFakeResourceType, "blah"). + WithTenancy(resource.DefaultNamespacedTenancy()). + Build() + id := resourceID("testing", "v1", "fake", "foo") + suite.idMod.EXPECT(). + Execute(mock.Anything, suite.rt, suite.res.Id). + Return(id, nil). + Once() + + suite.cache.EXPECT(). + Get(suite.res.Id.Type, "fake-index", id). + Return(out, nil). + Once() + + reqs, err := CacheGetMapper(suite.res.Id.Type, "fake-index", suite.idMod.Execute)( + context.Background(), + suite.rt, + suite.res, + ) + + require.NoError(suite.T(), err) + require.Len(suite.T(), reqs, 1) + prototest.AssertDeepEqual(suite.T(), out.Id, reqs[0].ID) +} + +func (suite *cacheSuite) TestListMapper_ModErr() { + suite.idMod.EXPECT(). + Execute(mock.Anything, suite.rt, suite.res.Id). + Return(nil, injectedErr). + Once() + + reqs, err := CacheListMapper(suite.res.Id.Type, "doesnt-matter", suite.idMod.Execute)( + context.Background(), + suite.rt, + suite.res, + ) + + require.Nil(suite.T(), reqs) + require.ErrorIs(suite.T(), err, injectedErr) +} + +func (suite *cacheSuite) TestListMapper_CacheErr() { + id := resourceID("testing", "v1", "fake", "foo") + suite.idMod.EXPECT(). + Execute(mock.Anything, suite.rt, suite.res.Id). + Return(id, nil). + Once() + + suite.cache.EXPECT(). + ListIterator(suite.res.Id.Type, "fake-index", id). + Return(nil, injectedErr). + Once() + + reqs, err := CacheListMapper(suite.res.Id.Type, "fake-index", suite.idMod.Execute)( + context.Background(), + suite.rt, + suite.res, + ) + + require.Nil(suite.T(), reqs) + require.ErrorIs(suite.T(), err, injectedErr) +} + +func (suite *cacheSuite) TestListMapper_Ok() { + out := resourcetest.Resource(altFakeResourceType, "blah"). + WithTenancy(resource.DefaultNamespacedTenancy()). + Build() + out2 := resourcetest.Resource(altFakeResourceType, "blah2"). + WithTenancy(resource.DefaultNamespacedTenancy()). + Build() + id := resourceID("testing", "v1", "fake", "foo") + suite.idMod.EXPECT(). + Execute(mock.Anything, suite.rt, suite.res.Id). + Return(id, nil). + Once() + + mockIter := cachemock.NewResourceIterator(suite.T()) + mockIter.EXPECT().Next().Return(out).Once() + mockIter.EXPECT().Next().Return(out2).Once() + mockIter.EXPECT().Next().Return(nil).Once() + + suite.cache.EXPECT(). + ListIterator(suite.res.Id.Type, "fake-index", id). + Return(mockIter, nil). + Once() + + reqs, err := CacheListMapper(suite.res.Id.Type, "fake-index", suite.idMod.Execute)( + context.Background(), + suite.rt, + suite.res, + ) + + require.NoError(suite.T(), err) + require.Len(suite.T(), reqs, 2) + expected := []controller.Request{ + {ID: out.Id}, + {ID: out2.Id}, + } + prototest.AssertElementsMatch(suite.T(), expected, reqs) +} + +func (suite *cacheSuite) TestParentsMapper_ModErr() { + suite.idMod.EXPECT(). + Execute(mock.Anything, suite.rt, suite.res.Id). + Return(nil, injectedErr). + Once() + + reqs, err := CacheParentsMapper(suite.res.Id.Type, "doesnt-matter", suite.idMod.Execute)( + context.Background(), + suite.rt, + suite.res, + ) + + require.Nil(suite.T(), reqs) + require.ErrorIs(suite.T(), err, injectedErr) +} + +func (suite *cacheSuite) TestParentsMapper_CacheErr() { + id := resourceID("testing", "v1", "fake", "foo") + suite.idMod.EXPECT(). + Execute(mock.Anything, suite.rt, suite.res.Id). + Return(id, nil). + Once() + + suite.cache.EXPECT(). + ParentsIterator(suite.res.Id.Type, "fake-index", id). + Return(nil, injectedErr). + Once() + + reqs, err := CacheParentsMapper(suite.res.Id.Type, "fake-index", suite.idMod.Execute)( + context.Background(), + suite.rt, + suite.res, + ) + + require.Nil(suite.T(), reqs) + require.ErrorIs(suite.T(), err, injectedErr) +} + +func (suite *cacheSuite) TestParentsMapper_Ok() { + out := resourcetest.Resource(altFakeResourceType, "blah"). + WithTenancy(resource.DefaultNamespacedTenancy()). + Build() + out2 := resourcetest.Resource(altFakeResourceType, "blah2"). + WithTenancy(resource.DefaultNamespacedTenancy()). + Build() + id := resourceID("testing", "v1", "fake", "foo") + suite.idMod.EXPECT(). + Execute(mock.Anything, suite.rt, suite.res.Id). + Return(id, nil). + Once() + + mockIter := cachemock.NewResourceIterator(suite.T()) + mockIter.EXPECT().Next().Return(out).Once() + mockIter.EXPECT().Next().Return(out2).Once() + mockIter.EXPECT().Next().Return(nil).Once() + + suite.cache.EXPECT(). + ParentsIterator(suite.res.Id.Type, "fake-index", id). + Return(mockIter, nil). + Once() + + reqs, err := CacheParentsMapper(suite.res.Id.Type, "fake-index", suite.idMod.Execute)( + context.Background(), + suite.rt, + suite.res, + ) + + require.NoError(suite.T(), err) + require.Len(suite.T(), reqs, 2) + expected := []controller.Request{ + {ID: out.Id}, + {ID: out2.Id}, + } + prototest.AssertElementsMatch(suite.T(), expected, reqs) +} + +func (suite *cacheSuite) TestListTransform_ModErr() { + suite.idMod.EXPECT(). + Execute(mock.Anything, suite.rt, suite.res.Id). + Return(nil, injectedErr). + Once() + + reqs, err := CacheListTransform(suite.res.Id.Type, "doesnt-matter", suite.idMod.Execute)( + context.Background(), + suite.rt, + suite.res, + ) + + require.Nil(suite.T(), reqs) + require.ErrorIs(suite.T(), err, injectedErr) +} + +func (suite *cacheSuite) TestListTransform_CacheErr() { + id := resourceID("testing", "v1", "fake", "foo") + suite.idMod.EXPECT(). + Execute(mock.Anything, suite.rt, suite.res.Id). + Return(id, nil). + Once() + + suite.cache.EXPECT(). + List(suite.res.Id.Type, "fake-index", id). + Return(nil, injectedErr). + Once() + + resources, err := CacheListTransform(suite.res.Id.Type, "fake-index", suite.idMod.Execute)( + context.Background(), + suite.rt, + suite.res, + ) + + require.Nil(suite.T(), resources) + require.ErrorIs(suite.T(), err, injectedErr) +} + +func (suite *cacheSuite) TestListTransform_Ok() { + out := resourcetest.Resource(altFakeResourceType, "blah"). + WithTenancy(resource.DefaultNamespacedTenancy()). + Build() + out2 := resourcetest.Resource(altFakeResourceType, "blah2"). + WithTenancy(resource.DefaultNamespacedTenancy()). + Build() + id := resourceID("testing", "v1", "fake", "foo") + suite.idMod.EXPECT(). + Execute(mock.Anything, suite.rt, suite.res.Id). + Return(id, nil). + Once() + + expected := []*pbresource.Resource{out, out2} + suite.cache.EXPECT(). + List(suite.res.Id.Type, "fake-index", id). + Return(expected, nil). + Once() + + resources, err := CacheListTransform(suite.res.Id.Type, "fake-index", suite.idMod.Execute)( + context.Background(), + suite.rt, + suite.res, + ) + + require.NoError(suite.T(), err) + prototest.AssertElementsMatch(suite.T(), expected, resources) +} + +func TestCacheDependencies(t *testing.T) { + suite.Run(t, new(cacheSuite)) +} + +func TestReplaceCacheIDType(t *testing.T) { + rt := controller.Runtime{ + // populate something to differentiate from zero value + Logger: hclog.Default(), + } + + in := resourceID("testing", "v1", "pre-mod", "foo") + + mod := ReplaceCacheIDType(fakeResourceType) + + out, err := mod(context.Background(), rt, in) + require.NoError(t, err) + prototest.AssertDeepEqual(t, resource.ReplaceType(fakeResourceType, in), out) + +} diff --git a/internal/controller/dependency/dependencymock/mock_CacheIDModifier.go b/internal/controller/dependency/dependencymock/mock_CacheIDModifier.go new file mode 100644 index 0000000000..f9e582372d --- /dev/null +++ b/internal/controller/dependency/dependencymock/mock_CacheIDModifier.go @@ -0,0 +1,96 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package dependencymock + +import ( + context "context" + + controller "github.com/hashicorp/consul/internal/controller" + + mock "github.com/stretchr/testify/mock" + + pbresource "github.com/hashicorp/consul/proto-public/pbresource" +) + +// CacheIDModifier is an autogenerated mock type for the CacheIDModifier type +type CacheIDModifier struct { + mock.Mock +} + +type CacheIDModifier_Expecter struct { + mock *mock.Mock +} + +func (_m *CacheIDModifier) EXPECT() *CacheIDModifier_Expecter { + return &CacheIDModifier_Expecter{mock: &_m.Mock} +} + +// Execute provides a mock function with given fields: ctx, rt, id +func (_m *CacheIDModifier) Execute(ctx context.Context, rt controller.Runtime, id *pbresource.ID) (*pbresource.ID, error) { + ret := _m.Called(ctx, rt, id) + + var r0 *pbresource.ID + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, controller.Runtime, *pbresource.ID) (*pbresource.ID, error)); ok { + return rf(ctx, rt, id) + } + if rf, ok := ret.Get(0).(func(context.Context, controller.Runtime, *pbresource.ID) *pbresource.ID); ok { + r0 = rf(ctx, rt, id) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pbresource.ID) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, controller.Runtime, *pbresource.ID) error); ok { + r1 = rf(ctx, rt, id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// CacheIDModifier_Execute_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Execute' +type CacheIDModifier_Execute_Call struct { + *mock.Call +} + +// Execute is a helper method to define mock.On call +// - ctx context.Context +// - rt controller.Runtime +// - id *pbresource.ID +func (_e *CacheIDModifier_Expecter) Execute(ctx interface{}, rt interface{}, id interface{}) *CacheIDModifier_Execute_Call { + return &CacheIDModifier_Execute_Call{Call: _e.mock.On("Execute", ctx, rt, id)} +} + +func (_c *CacheIDModifier_Execute_Call) Run(run func(ctx context.Context, rt controller.Runtime, id *pbresource.ID)) *CacheIDModifier_Execute_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(controller.Runtime), args[2].(*pbresource.ID)) + }) + return _c +} + +func (_c *CacheIDModifier_Execute_Call) Return(_a0 *pbresource.ID, _a1 error) *CacheIDModifier_Execute_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *CacheIDModifier_Execute_Call) RunAndReturn(run func(context.Context, controller.Runtime, *pbresource.ID) (*pbresource.ID, error)) *CacheIDModifier_Execute_Call { + _c.Call.Return(run) + return _c +} + +// NewCacheIDModifier creates a new instance of CacheIDModifier. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewCacheIDModifier(t interface { + mock.TestingT + Cleanup(func()) +}) *CacheIDModifier { + mock := &CacheIDModifier{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/controller/dependency/dependencymock/mock_DependencyTransform.go b/internal/controller/dependency/dependencymock/mock_DependencyTransform.go new file mode 100644 index 0000000000..13168f2bbb --- /dev/null +++ b/internal/controller/dependency/dependencymock/mock_DependencyTransform.go @@ -0,0 +1,96 @@ +// Code generated by mockery v2.37.1. DO NOT EDIT. + +package dependencymock + +import ( + context "context" + + controller "github.com/hashicorp/consul/internal/controller" + + mock "github.com/stretchr/testify/mock" + + pbresource "github.com/hashicorp/consul/proto-public/pbresource" +) + +// DependencyTransform is an autogenerated mock type for the DependencyTransform type +type DependencyTransform struct { + mock.Mock +} + +type DependencyTransform_Expecter struct { + mock *mock.Mock +} + +func (_m *DependencyTransform) EXPECT() *DependencyTransform_Expecter { + return &DependencyTransform_Expecter{mock: &_m.Mock} +} + +// Execute provides a mock function with given fields: ctx, rt, res +func (_m *DependencyTransform) Execute(ctx context.Context, rt controller.Runtime, res *pbresource.Resource) ([]*pbresource.Resource, error) { + ret := _m.Called(ctx, rt, res) + + var r0 []*pbresource.Resource + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, controller.Runtime, *pbresource.Resource) ([]*pbresource.Resource, error)); ok { + return rf(ctx, rt, res) + } + if rf, ok := ret.Get(0).(func(context.Context, controller.Runtime, *pbresource.Resource) []*pbresource.Resource); ok { + r0 = rf(ctx, rt, res) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*pbresource.Resource) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, controller.Runtime, *pbresource.Resource) error); ok { + r1 = rf(ctx, rt, res) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// DependencyTransform_Execute_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Execute' +type DependencyTransform_Execute_Call struct { + *mock.Call +} + +// Execute is a helper method to define mock.On call +// - ctx context.Context +// - rt controller.Runtime +// - res *pbresource.Resource +func (_e *DependencyTransform_Expecter) Execute(ctx interface{}, rt interface{}, res interface{}) *DependencyTransform_Execute_Call { + return &DependencyTransform_Execute_Call{Call: _e.mock.On("Execute", ctx, rt, res)} +} + +func (_c *DependencyTransform_Execute_Call) Run(run func(ctx context.Context, rt controller.Runtime, res *pbresource.Resource)) *DependencyTransform_Execute_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(controller.Runtime), args[2].(*pbresource.Resource)) + }) + return _c +} + +func (_c *DependencyTransform_Execute_Call) Return(_a0 []*pbresource.Resource, _a1 error) *DependencyTransform_Execute_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *DependencyTransform_Execute_Call) RunAndReturn(run func(context.Context, controller.Runtime, *pbresource.Resource) ([]*pbresource.Resource, error)) *DependencyTransform_Execute_Call { + _c.Call.Return(run) + return _c +} + +// NewDependencyTransform creates a new instance of DependencyTransform. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewDependencyTransform(t interface { + mock.TestingT + Cleanup(func()) +}) *DependencyTransform { + mock := &DependencyTransform{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/controller/dependency/higher_order.go b/internal/controller/dependency/higher_order.go new file mode 100644 index 0000000000..2f2e4c8e3c --- /dev/null +++ b/internal/controller/dependency/higher_order.go @@ -0,0 +1,47 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package dependency + +import ( + "context" + + "github.com/hashicorp/consul/internal/controller" + "github.com/hashicorp/consul/internal/resource" + "github.com/hashicorp/consul/proto-public/pbresource" +) + +// WrapAndReplaceType will invoke the provided dependency mapper and then replace +// the type of all generated requests with the desired type. +func WrapAndReplaceType(desiredType *pbresource.Type, mapper controller.DependencyMapper) controller.DependencyMapper { + return func(ctx context.Context, rt controller.Runtime, res *pbresource.Resource) ([]controller.Request, error) { + reqs, err := mapper(ctx, rt, res) + if err != nil { + return nil, err + } + + for idx, req := range reqs { + req.ID = resource.ReplaceType(desiredType, req.ID) + reqs[idx] = req + } + return reqs, nil + } +} + +// MultiMapper can be used to concatenate the results of multiple dependency mappers. This +// helps to allow composition of different relationships without having to implement larger +// functions to perform all the various mappings. The goal here being to enable more reusuable +// dependency mappers. +func MultiMapper(mappers ...controller.DependencyMapper) controller.DependencyMapper { + return func(ctx context.Context, rt controller.Runtime, res *pbresource.Resource) ([]controller.Request, error) { + var results []controller.Request + for _, mapper := range mappers { + reqs, err := mapper(ctx, rt, res) + if err != nil { + return nil, err + } + results = append(results, reqs...) + } + return results, nil + } +} diff --git a/internal/controller/dependency/higher_order_test.go b/internal/controller/dependency/higher_order_test.go new file mode 100644 index 0000000000..0302235646 --- /dev/null +++ b/internal/controller/dependency/higher_order_test.go @@ -0,0 +1,144 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package dependency + +import ( + "context" + "errors" + "testing" + + "github.com/hashicorp/consul/internal/controller" + "github.com/hashicorp/consul/internal/controller/controllermock" + "github.com/hashicorp/go-hclog" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + "github.com/hashicorp/consul/internal/resource/resourcetest" + "github.com/hashicorp/consul/proto-public/pbresource" + "github.com/hashicorp/consul/proto/private/prototest" +) + +var ( + fakeMapType = &pbresource.Type{ + Group: "testing", + GroupVersion: "v1", + Kind: "pre-map", + } + + fakeResourceType = &pbresource.Type{ + Group: "testing", + GroupVersion: "v1", + Kind: "fake", + } + + altFakeResourceType = &pbresource.Type{ + Group: "testing", + GroupVersion: "v1", + Kind: "alt-fake", + } + + injectedErr = errors.New("injected") +) + +func TestWrapAndReplaceType(t *testing.T) { + res := resourcetest.Resource(fakeMapType, "something").Build() + // populating the runtime with something so we can tell that + // the runtime is passed through + rt := controller.Runtime{ + Logger: hclog.Default(), + } + + t.Run("ok", func(t *testing.T) { + mm := controllermock.NewDependencyMapper(t) + mm.EXPECT(). + Execute(mock.Anything, rt, res). + Return([]controller.Request{ + {ID: resourceID("testing", "v1", "fake", "foo")}, + {ID: resourceID("testing", "v1", "fake", "bar")}, + }, nil). + Once() + + mapper := WrapAndReplaceType(altFakeResourceType, mm.Execute) + reqs, err := mapper(context.Background(), rt, res) + require.NoError(t, err) + require.Len(t, reqs, 2) + expected := []controller.Request{ + {ID: resourceID("testing", "v1", "alt-fake", "foo")}, + {ID: resourceID("testing", "v1", "alt-fake", "bar")}, + } + prototest.AssertElementsMatch(t, expected, reqs) + }) + + t.Run("err", func(t *testing.T) { + mm := controllermock.NewDependencyMapper(t) + mm.EXPECT(). + Execute(mock.Anything, rt, res). + Return(nil, injectedErr). + Once() + mapper := WrapAndReplaceType(altFakeResourceType, mm.Execute) + reqs, err := mapper(context.Background(), rt, res) + require.Nil(t, reqs) + require.ErrorIs(t, err, injectedErr) + }) +} + +func TestMultiMapper(t *testing.T) { + res := resourcetest.Resource(fakeMapType, "something").Build() + // populating the runtime with something so we can tell that + // the runtime is passed through + rt := controller.Runtime{ + Logger: hclog.Default(), + } + + t.Run("ok", func(t *testing.T) { + mockMapper := controllermock.NewDependencyMapper(t) + mockMapper.EXPECT(). + Execute(mock.Anything, rt, res). + Return([]controller.Request{ + {ID: resourceID("testing", "v1", "fake", "foo")}, + {ID: resourceID("testing", "v1", "fake", "bar")}, + }, nil). + Times(2) + + mm := MultiMapper( + mockMapper.Execute, + WrapAndReplaceType(altFakeResourceType, mockMapper.Execute), + ) + + reqs, err := mm(context.Background(), rt, res) + require.NoError(t, err) + require.Len(t, reqs, 4) + expected := []controller.Request{ + {ID: resourceID("testing", "v1", "alt-fake", "foo")}, + {ID: resourceID("testing", "v1", "alt-fake", "bar")}, + {ID: resourceID("testing", "v1", "fake", "foo")}, + {ID: resourceID("testing", "v1", "fake", "bar")}, + } + prototest.AssertElementsMatch(t, expected, reqs) + }) + + t.Run("err", func(t *testing.T) { + mockMapper := controllermock.NewDependencyMapper(t) + mockMapper.EXPECT(). + Execute(mock.Anything, rt, res). + Return([]controller.Request{ + {ID: resourceID("testing", "v1", "fake", "foo")}, + {ID: resourceID("testing", "v1", "fake", "bar")}, + }, nil). + Once() + + mockMapper.EXPECT(). + Execute(mock.Anything, rt, res). + Return(nil, injectedErr). + Once() + + mm := MultiMapper( + mockMapper.Execute, + WrapAndReplaceType(altFakeResourceType, mockMapper.Execute), + ) + reqs, err := mm(context.Background(), rt, res) + require.Nil(t, reqs) + require.ErrorIs(t, err, injectedErr) + }) +} diff --git a/internal/controller/dependency/simple.go b/internal/controller/dependency/simple.go new file mode 100644 index 0000000000..b154487e29 --- /dev/null +++ b/internal/controller/dependency/simple.go @@ -0,0 +1,54 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package dependency + +import ( + "context" + + "github.com/hashicorp/consul/internal/controller" + "github.com/hashicorp/consul/internal/resource" + "github.com/hashicorp/consul/proto-public/pbresource" +) + +// MapOwner implements a DependencyMapper that returns the updated resource's owner. +func MapOwner(_ context.Context, _ controller.Runtime, res *pbresource.Resource) ([]controller.Request, error) { + var reqs []controller.Request + if res.Owner != nil { + reqs = append(reqs, controller.Request{ID: res.Owner}) + } + return reqs, nil +} + +// MapOwnerFiltered creates a DependencyMapper that returns owner IDs as Requests +// if the type of the owner ID matches the given filter type. +func MapOwnerFiltered(filter *pbresource.Type) controller.DependencyMapper { + return func(_ context.Context, _ controller.Runtime, res *pbresource.Resource) ([]controller.Request, error) { + if res.Owner == nil { + return nil, nil + } + + if !resource.EqualType(res.Owner.GetType(), filter) { + return nil, nil + } + + return []controller.Request{{ID: res.Owner}}, nil + } +} + +// ReplaceType creates a DependencyMapper that returns request IDs with the same +// name and tenancy as the original resource but with the type replaced with +// the type specified as this functions parameter. +func ReplaceType(desiredType *pbresource.Type) controller.DependencyMapper { + return func(_ context.Context, _ controller.Runtime, res *pbresource.Resource) ([]controller.Request, error) { + return []controller.Request{ + { + ID: &pbresource.ID{ + Type: desiredType, + Tenancy: res.Id.Tenancy, + Name: res.Id.Name, + }, + }, + }, nil + } +} diff --git a/internal/controller/dependency_mappers_test.go b/internal/controller/dependency/simple_test.go similarity index 91% rename from internal/controller/dependency_mappers_test.go rename to internal/controller/dependency/simple_test.go index b9956cdb71..b17b1fab8a 100644 --- a/internal/controller/dependency_mappers_test.go +++ b/internal/controller/dependency/simple_test.go @@ -1,13 +1,14 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: BUSL-1.1 -package controller +package dependency import ( "context" "testing" "github.com/google/go-cmp/cmp/cmpopts" + "github.com/hashicorp/consul/internal/controller" "github.com/hashicorp/consul/proto-public/pbresource" "github.com/hashicorp/consul/proto/private/prototest" "github.com/stretchr/testify/require" @@ -37,7 +38,7 @@ func TestMapOwner(t *testing.T) { Owner: owner, } - reqs, err := MapOwner(context.Background(), Runtime{}, res) + reqs, err := MapOwner(context.Background(), controller.Runtime{}, res) require.NoError(t, err) require.Len(t, reqs, 1) prototest.AssertDeepEqual(t, owner, reqs[0].ID) @@ -81,7 +82,7 @@ func TestMapOwnerFiltered(t *testing.T) { for name, tcase := range cases { t.Run(name, func(t *testing.T) { // the runtime is not used by the mapper so its fine to pass an empty struct - req, err := mapper(context.Background(), Runtime{}, &pbresource.Resource{ + req, err := mapper(context.Background(), controller.Runtime{}, &pbresource.Resource{ Id: resourceID("foo", "v1", "other", "x"), Owner: tcase.owner, }) @@ -127,7 +128,7 @@ func TestReplaceType(t *testing.T) { mapper := ReplaceType(rtype) - reqs, err := mapper(nil, Runtime{}, in) + reqs, err := mapper(nil, controller.Runtime{}, in) require.NoError(t, err) require.Len(t, reqs, 1) diff --git a/internal/controller/dependency/transform.go b/internal/controller/dependency/transform.go new file mode 100644 index 0000000000..9edff3fb81 --- /dev/null +++ b/internal/controller/dependency/transform.go @@ -0,0 +1,74 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package dependency + +import ( + "context" + + "github.com/hashicorp/consul/internal/controller" + "github.com/hashicorp/consul/proto-public/pbresource" +) + +// DependencyTransform is used when the incoming resource from a watch needs to +// be transformed to a different resource or set of resources and then have +// a DependencyMapper executed on each element in that result set. This should +// only be needed for dealing with more complex dependency relationships where +// the managed type and watched type are not directly related. +type DependencyTransform func( + ctx context.Context, + rt controller.Runtime, + res *pbresource.Resource, +) ([]*pbresource.Resource, error) + +// MapperWithTransform will execute the provided DependencyTransform and then execute +// the provided DependencyMapper once for each of the resources output by the transform. +// The DependencyMapper outputs will then be concatenated together to form the whole +// set of mapped Requests. +func MapperWithTransform(mapper controller.DependencyMapper, transform DependencyTransform) controller.DependencyMapper { + return func(ctx context.Context, rt controller.Runtime, res *pbresource.Resource) ([]controller.Request, error) { + transformed, err := transform(ctx, rt, res) + if err != nil { + return nil, err + } + + var reqs []controller.Request + for _, res := range transformed { + newReqs, err := mapper(ctx, rt, res) + if err != nil { + return nil, err + } + + reqs = append(reqs, newReqs...) + } + + return reqs, nil + } +} + +// TransformChain takes a set of transformers and will execute them as a pipeline. +// The first transformer will output some resources. Those resources will then be +// used as inputs to the next transform. The chain will then continue until all +// transformers have been run. +func TransformChain(transformers ...DependencyTransform) DependencyTransform { + return func(ctx context.Context, rt controller.Runtime, res *pbresource.Resource) ([]*pbresource.Resource, error) { + toTransform := []*pbresource.Resource{res} + + for _, transform := range transformers { + var nextResources []*pbresource.Resource + + for _, res := range toTransform { + next, err := transform(ctx, rt, res) + if err != nil { + return nil, err + } + + nextResources = append(nextResources, next...) + } + + toTransform = nextResources + } + + return toTransform, nil + } +} diff --git a/internal/controller/dependency/transform_test.go b/internal/controller/dependency/transform_test.go new file mode 100644 index 0000000000..abdc9f9919 --- /dev/null +++ b/internal/controller/dependency/transform_test.go @@ -0,0 +1,166 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package dependency + +import ( + "context" + "testing" + + "github.com/hashicorp/consul/internal/controller" + "github.com/hashicorp/consul/internal/controller/controllermock" + "github.com/hashicorp/consul/internal/controller/dependency/dependencymock" + "github.com/hashicorp/consul/internal/resource/resourcetest" + "github.com/hashicorp/consul/proto-public/pbresource" + "github.com/hashicorp/consul/proto/private/prototest" + "github.com/hashicorp/go-hclog" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +func TestMapperWithTransform(t *testing.T) { + res := resourcetest.Resource(fakeMapType, "something").Build() + rt := controller.Runtime{ + // populating some field to differentiate from zero value + Logger: hclog.Default(), + } + transformed1 := resourcetest.Resource(fakeResourceType, "foo").Build() + transformed2 := resourcetest.Resource(fakeResourceType, "bar").Build() + + t.Run("transform-err", func(t *testing.T) { + mockTransform := dependencymock.NewDependencyTransform(t) + mockTransform.EXPECT(). + Execute(mock.Anything, rt, res). + Return(nil, injectedErr). + Once() + + mockMapper := controllermock.NewDependencyMapper(t) + + mt := MapperWithTransform(mockMapper.Execute, mockTransform.Execute) + reqs, err := mt(context.Background(), rt, res) + require.Nil(t, reqs) + require.ErrorIs(t, err, injectedErr) + }) + + t.Run("mapper-err", func(t *testing.T) { + mockTransform := dependencymock.NewDependencyTransform(t) + mockTransform.EXPECT(). + Execute(mock.Anything, rt, res). + Return([]*pbresource.Resource{transformed1, transformed2}, nil). + Once() + + mockMapper := controllermock.NewDependencyMapper(t) + mockMapper.EXPECT(). + Execute(mock.Anything, rt, transformed1). + Return(nil, injectedErr). + Once() + + mt := MapperWithTransform(mockMapper.Execute, mockTransform.Execute) + reqs, err := mt(context.Background(), rt, res) + require.Nil(t, reqs) + require.ErrorIs(t, err, injectedErr) + }) + + t.Run("ok", func(t *testing.T) { + mockTransform := dependencymock.NewDependencyTransform(t) + mockTransform.EXPECT(). + Execute(mock.Anything, rt, res). + Return([]*pbresource.Resource{transformed1, transformed2}, nil). + Once() + + mockMapper := controllermock.NewDependencyMapper(t) + mockMapper.EXPECT(). + Execute(mock.Anything, rt, transformed1). + Return([]controller.Request{ + {ID: resourceID("testing", "v1", "alt-fake", "foo")}, + {ID: resourceID("testing", "v1", "alt-fake", "bar")}, + }, nil). + Once() + + mockMapper.EXPECT(). + Execute(mock.Anything, rt, transformed2). + Return([]controller.Request{ + {ID: resourceID("testing", "v1", "alt-fake", "foo2")}, + {ID: resourceID("testing", "v1", "alt-fake", "bar2")}, + }, nil). + Once() + + mt := MapperWithTransform(mockMapper.Execute, mockTransform.Execute) + reqs, err := mt(context.Background(), rt, res) + require.NoError(t, err) + require.Len(t, reqs, 4) + expected := []controller.Request{ + {ID: resourceID("testing", "v1", "alt-fake", "foo")}, + {ID: resourceID("testing", "v1", "alt-fake", "bar")}, + {ID: resourceID("testing", "v1", "alt-fake", "foo2")}, + {ID: resourceID("testing", "v1", "alt-fake", "bar2")}, + } + prototest.AssertElementsMatch(t, expected, reqs) + }) +} + +func TestTransformChain(t *testing.T) { + res := resourcetest.Resource(fakeMapType, "something").Build() + rt := controller.Runtime{ + // populating some field to differentiate from zero value + Logger: hclog.Default(), + } + transformed1 := resourcetest.Resource(fakeResourceType, "foo").Build() + transformed2 := resourcetest.Resource(fakeResourceType, "bar").Build() + + out1 := resourcetest.Resource(altFakeResourceType, "foo").Build() + out2 := resourcetest.Resource(altFakeResourceType, "bar").Build() + out3 := resourcetest.Resource(altFakeResourceType, "baz").Build() + + t.Run("err", func(t *testing.T) { + mockTransform := dependencymock.NewDependencyTransform(t) + mockTransform.EXPECT(). + Execute(mock.Anything, rt, res). + Return([]*pbresource.Resource{transformed1}, nil). + Once() + mockTransform.EXPECT(). + Execute(mock.Anything, rt, transformed1). + Return(nil, injectedErr). + Once() + + resources, err := TransformChain(mockTransform.Execute, mockTransform.Execute)( + context.Background(), + rt, + res, + ) + + require.Nil(t, resources) + require.ErrorIs(t, err, injectedErr) + }) + + t.Run("ok", func(t *testing.T) { + mockTransform := dependencymock.NewDependencyTransform(t) + // Transform Chain + // + // 1. Transform original res to the two outputs + // 2. Transform the first output from 1 + // 3. Transform the second output from 1 + mockTransform.EXPECT(). + Execute(mock.Anything, rt, res). + Return([]*pbresource.Resource{transformed1, transformed2}, nil). + Once() + mockTransform.EXPECT(). + Execute(mock.Anything, rt, transformed1). + Return([]*pbresource.Resource{out1, out2}, nil). + Once() + mockTransform.EXPECT(). + Execute(mock.Anything, rt, transformed2). + Return([]*pbresource.Resource{out3}, nil). + Once() + + resources, err := TransformChain(mockTransform.Execute, mockTransform.Execute)( + context.Background(), + rt, + res, + ) + + require.NoError(t, err) + expected := []*pbresource.Resource{out1, out2, out3} + prototest.AssertElementsMatch(t, expected, resources) + }) +} diff --git a/internal/controller/dependency_mappers.go b/internal/controller/dependency_mappers.go deleted file mode 100644 index 1ac331ffaf..0000000000 --- a/internal/controller/dependency_mappers.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package controller - -import ( - "context" - - "github.com/hashicorp/consul/internal/resource" - "github.com/hashicorp/consul/proto-public/pbresource" -) - -// DependencyMapper is called when a dependency watched via WithWatch is changed -// to determine which of the controller's managed resources need to be reconciled. -type DependencyMapper func( - ctx context.Context, - rt Runtime, - res *pbresource.Resource, -) ([]Request, error) - -// CustomDependencyMapper is called when an Event occurs to determine which of the -// controller's managed resources need to be reconciled. -type CustomDependencyMapper func( - ctx context.Context, - rt Runtime, - event Event, -) ([]Request, error) - -// MapOwner implements a DependencyMapper that returns the updated resource's owner. -func MapOwner(_ context.Context, _ Runtime, res *pbresource.Resource) ([]Request, error) { - var reqs []Request - if res.Owner != nil { - reqs = append(reqs, Request{ID: res.Owner}) - } - return reqs, nil -} - -// MapOwnerFiltered creates a DependencyMapper that returns owner IDs as Requests -// if the type of the owner ID matches the given filter type. -func MapOwnerFiltered(filter *pbresource.Type) DependencyMapper { - return func(_ context.Context, _ Runtime, res *pbresource.Resource) ([]Request, error) { - if res.Owner == nil { - return nil, nil - } - - if !resource.EqualType(res.Owner.GetType(), filter) { - return nil, nil - } - - return []Request{{ID: res.Owner}}, nil - } -} - -// ReplaceType creates a DependencyMapper that returns request IDs with the same -// name and tenancy as the original resource but with the type replaced with -// the type specified as this functions parameter. -func ReplaceType(desiredType *pbresource.Type) DependencyMapper { - return func(_ context.Context, _ Runtime, res *pbresource.Resource) ([]Request, error) { - return []Request{ - { - ID: &pbresource.ID{ - Type: desiredType, - Tenancy: res.Id.Tenancy, - Name: res.Id.Name, - }, - }, - }, nil - } -} diff --git a/internal/controller/manager.go b/internal/controller/manager.go index 2e46b21400..8e42bf6260 100644 --- a/internal/controller/manager.go +++ b/internal/controller/manager.go @@ -23,7 +23,7 @@ type Manager struct { mu sync.Mutex running bool - controllers []Controller + controllers []*Controller leaseChans []chan struct{} } @@ -38,7 +38,7 @@ func NewManager(client pbresource.ResourceServiceClient, logger hclog.Logger) *M // Register the given controller to be executed by the Manager. Cannot be called // once the Manager is running. -func (m *Manager) Register(ctrl Controller) { +func (m *Manager) Register(ctrl *Controller) { m.mu.Lock() defer m.mu.Unlock() @@ -65,16 +65,7 @@ func (m *Manager) Run(ctx context.Context) { m.running = true for _, desc := range m.controllers { - logger := desc.logger - if logger == nil { - logger = m.logger.With("managed_type", desc.managedType.Kind) - } - - runner := &controllerRunner{ - ctrl: desc, - client: m.client, - logger: logger, - } + runner := newControllerRunner(desc, m.client, m.logger) go newSupervisor(runner.run, m.newLeaseLocked(desc)).run(ctx) } } @@ -100,7 +91,7 @@ func (m *Manager) SetRaftLeader(leader bool) { } } -func (m *Manager) newLeaseLocked(ctrl Controller) Lease { +func (m *Manager) newLeaseLocked(ctrl *Controller) Lease { if ctrl.placement == PlacementEachServer { return eternalLease{} } diff --git a/internal/controller/runner.go b/internal/controller/runner.go new file mode 100644 index 0000000000..a59130906b --- /dev/null +++ b/internal/controller/runner.go @@ -0,0 +1,289 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package controller + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/hashicorp/go-hclog" + "golang.org/x/sync/errgroup" + + "github.com/hashicorp/consul/agent/consul/controller/queue" + "github.com/hashicorp/consul/internal/controller/cache" + "github.com/hashicorp/consul/internal/resource" + "github.com/hashicorp/consul/internal/storage" + "github.com/hashicorp/consul/proto-public/pbresource" +) + +// Runtime contains the dependencies required by reconcilers. +type Runtime struct { + Client pbresource.ResourceServiceClient + Logger hclog.Logger + Cache cache.ReadOnlyCache +} + +// controllerRunner contains the actual implementation of running a controller +// including creating watches, calling the reconciler, handling retries, etc. +type controllerRunner struct { + ctrl *Controller + client pbresource.ResourceServiceClient + logger hclog.Logger + cache cache.Cache +} + +func newControllerRunner(c *Controller, client pbresource.ResourceServiceClient, defaultLogger hclog.Logger) *controllerRunner { + return &controllerRunner{ + ctrl: c, + client: client, + logger: c.buildLogger(defaultLogger), + // Do not build the cache here. If we build/set it when the controller runs + // then if a controller is restarted it will invalidate the previous cache automatically. + } +} + +func (c *controllerRunner) run(ctx context.Context) error { + c.logger.Debug("controller running") + defer c.logger.Debug("controller stopping") + + c.cache = c.ctrl.buildCache() + + group, groupCtx := errgroup.WithContext(ctx) + recQueue := runQueue[Request](groupCtx, c.ctrl) + + // Managed Type Events → Reconciliation Queue + group.Go(func() error { + return c.watch(groupCtx, c.ctrl.managedTypeWatch.watchedType, func(res *pbresource.Resource) { + recQueue.Add(Request{ID: res.Id}) + }) + }) + + for _, w := range c.ctrl.watches { + mapQueue := runQueue[mapperRequest](groupCtx, c.ctrl) + watcher := w + + // Watched Type Events → Mapper Queue + group.Go(func() error { + return c.watch(groupCtx, watcher.watchedType, func(res *pbresource.Resource) { + mapQueue.Add(mapperRequest{res: res}) + }) + }) + + // Mapper Queue → Mapper → Reconciliation Queue + group.Go(func() error { + return c.runMapper(groupCtx, watcher, mapQueue, recQueue, func(ctx context.Context, runtime Runtime, itemType queue.ItemType) ([]Request, error) { + return watcher.mapper(ctx, runtime, itemType.(mapperRequest).res) + }) + }) + } + + for _, cw := range c.ctrl.customWatches { + customMapQueue := runQueue[Event](groupCtx, c.ctrl) + watcher := cw + // Custom Events → Mapper Queue + group.Go(func() error { + return watcher.source.Watch(groupCtx, func(e Event) { + customMapQueue.Add(e) + }) + }) + + // Mapper Queue → Mapper → Reconciliation Queue + group.Go(func() error { + return c.runCustomMapper(groupCtx, watcher, customMapQueue, recQueue, func(ctx context.Context, runtime Runtime, itemType queue.ItemType) ([]Request, error) { + return watcher.mapper(ctx, runtime, itemType.(Event)) + }) + }) + } + + // Reconciliation Queue → Reconciler + group.Go(func() error { + return c.runReconciler(groupCtx, recQueue) + }) + + return group.Wait() +} + +func runQueue[T queue.ItemType](ctx context.Context, ctrl *Controller) queue.WorkQueue[T] { + base, max := ctrl.backoff() + return queue.RunWorkQueue[T](ctx, base, max) +} + +func (c *controllerRunner) watch(ctx context.Context, typ *pbresource.Type, add func(*pbresource.Resource)) error { + wl, err := c.client.WatchList(ctx, &pbresource.WatchListRequest{ + Type: typ, + Tenancy: &pbresource.Tenancy{ + Partition: storage.Wildcard, + PeerName: storage.Wildcard, + Namespace: storage.Wildcard, + }, + }) + if err != nil { + c.logger.Error("failed to create watch", "error", err) + return err + } + + for { + event, err := wl.Recv() + if err != nil { + c.logger.Warn("error received from watch", "error", err) + return err + } + + // Keep the cache up to date. There main reason to do this here is + // to ensure that any mapper/reconciliation queue deduping wont + // hide events from being observed and updating the cache state. + // Therefore we should do this before any queueing. + switch event.Operation { + case pbresource.WatchEvent_OPERATION_UPSERT: + c.cache.Insert(event.Resource) + case pbresource.WatchEvent_OPERATION_DELETE: + c.cache.Delete(event.Resource) + } + + add(event.Resource) + } +} + +func (c *controllerRunner) runMapper( + ctx context.Context, + w *watch, + from queue.WorkQueue[mapperRequest], + to queue.WorkQueue[Request], + mapper func(ctx context.Context, runtime Runtime, itemType queue.ItemType) ([]Request, error), +) error { + logger := c.logger.With("watched_resource_type", resource.ToGVK(w.watchedType)) + + for { + item, shutdown := from.Get() + if shutdown { + return nil + } + + if err := c.doMap(ctx, mapper, to, item, logger); err != nil { + from.AddRateLimited(item) + from.Done(item) + continue + } + + from.Forget(item) + from.Done(item) + } +} + +func (c *controllerRunner) runCustomMapper( + ctx context.Context, + cw customWatch, + from queue.WorkQueue[Event], + to queue.WorkQueue[Request], + mapper func(ctx context.Context, runtime Runtime, itemType queue.ItemType) ([]Request, error), +) error { + logger := c.logger.With("watched_event", cw.source) + + for { + item, shutdown := from.Get() + if shutdown { + return nil + } + + if err := c.doMap(ctx, mapper, to, item, logger); err != nil { + from.AddRateLimited(item) + from.Done(item) + continue + } + + from.Forget(item) + from.Done(item) + } +} + +func (c *controllerRunner) doMap(ctx context.Context, mapper func(ctx context.Context, runtime Runtime, itemType queue.ItemType) ([]Request, error), to queue.WorkQueue[Request], item queue.ItemType, logger hclog.Logger) error { + var reqs []Request + if err := c.handlePanic(func() error { + var err error + reqs, err = mapper(ctx, c.runtime(logger.With("map-request-key", item.Key())), item) + return err + }); err != nil { + return err + } + + for _, r := range reqs { + if !resource.EqualType(r.ID.Type, c.ctrl.managedTypeWatch.watchedType) { + logger.Error("dependency mapper returned request for a resource of the wrong type", + "type_expected", resource.ToGVK(c.ctrl.managedTypeWatch.watchedType), + "type_got", resource.ToGVK(r.ID.Type), + ) + continue + } + to.Add(r) + } + return nil +} + +func (c *controllerRunner) runReconciler(ctx context.Context, queue queue.WorkQueue[Request]) error { + for { + req, shutdown := queue.Get() + if shutdown { + return nil + } + + c.logger.Trace("handling request", "request", req) + err := c.handlePanic(func() error { + return c.ctrl.reconciler.Reconcile(ctx, c.runtime(c.logger.With("resource-id", req.ID.String())), req) + }) + if err == nil { + queue.Forget(req) + } else { + var requeueAfter RequeueAfterError + if errors.As(err, &requeueAfter) { + queue.Forget(req) + queue.AddAfter(req, time.Duration(requeueAfter)) + } else { + queue.AddRateLimited(req) + } + } + queue.Done(req) + } +} + +func (c *controllerRunner) handlePanic(fn func() error) (err error) { + defer func() { + if r := recover(); r != nil { + stack := hclog.Stacktrace() + c.logger.Error("controller panic", + "panic", r, + "stack", stack, + ) + err = fmt.Errorf("panic [recovered]: %v", r) + return + } + }() + + return fn() +} + +func (c *controllerRunner) runtime(logger hclog.Logger) Runtime { + return Runtime{ + Client: c.client, + Logger: logger, + Cache: c.cache, + } +} + +type mapperRequest struct{ res *pbresource.Resource } + +// Key satisfies the queue.ItemType interface. It returns a string which will be +// used to de-duplicate requests in the queue. +func (i mapperRequest) Key() string { + return fmt.Sprintf( + "type=%q,part=%q,peer=%q,ns=%q,name=%q,uid=%q", + resource.ToGVK(i.res.Id.Type), + i.res.Id.Tenancy.Partition, + i.res.Id.Tenancy.PeerName, + i.res.Id.Tenancy.Namespace, + i.res.Id.Name, + i.res.Id.Uid, + ) +} diff --git a/internal/controller/testing.go b/internal/controller/testing.go new file mode 100644 index 0000000000..2e6a543d4c --- /dev/null +++ b/internal/controller/testing.go @@ -0,0 +1,57 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package controller + +import ( + "context" + + "github.com/hashicorp/consul/internal/controller/cache" + "github.com/hashicorp/consul/proto-public/pbresource" + "github.com/hashicorp/go-hclog" +) + +// TestController is most useful when writing unit tests for a controller where +// individual Reconcile calls need to be made instead of having a Manager +// execute the controller in response to watch events. +type TestController struct { + c *Controller + cache cache.Cache + client pbresource.ResourceServiceClient + logger hclog.Logger +} + +// NewTestController will create a new TestController from the provided Controller +// and ResourceServiceClient. The test controller will build the controllers +// cache with the configured indexes and will maintain the cached state in response +// to Write, WriteStatus and Delete calls made on the wrapped ResourceServiceClient. +// Call the Runtime() method to get at the wrapped client. +func NewTestController(ctl *Controller, client pbresource.ResourceServiceClient) *TestController { + ctlCache := ctl.buildCache() + return &TestController{ + c: ctl, + cache: ctlCache, + client: cache.NewCachedClient(ctlCache, client), + logger: ctl.buildLogger(hclog.NewNullLogger()), + } +} + +// Reconcile invokes the controllers configured reconciler with the cache enabled Runtime +func (tc *TestController) Reconcile(ctx context.Context, req Request) error { + return tc.c.reconciler.Reconcile(ctx, tc.Runtime(), req) +} + +// Reconciler returns the controllers configured reconciler +func (tc *TestController) Reconciler() Reconciler { + return tc.c.reconciler +} + +// Runtime returns the Runtime that should be used for calls to reconcile or to perform +// operations that would also affect the managed cache. +func (tc *TestController) Runtime() Runtime { + return Runtime{ + Client: tc.client, + Logger: tc.logger, + Cache: tc.cache, + } +} diff --git a/internal/controller/watch.go b/internal/controller/watch.go new file mode 100644 index 0000000000..0fd821daad --- /dev/null +++ b/internal/controller/watch.go @@ -0,0 +1,42 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package controller + +import ( + "fmt" + + "github.com/hashicorp/consul/internal/controller/cache/index" + "github.com/hashicorp/consul/internal/resource" + "github.com/hashicorp/consul/proto-public/pbresource" +) + +type watch struct { + watchedType *pbresource.Type + mapper DependencyMapper + indexes map[string]*index.Index +} + +func newWatch(watchedType *pbresource.Type, mapper DependencyMapper) *watch { + if mapper == nil { + panic("mapper not provided") + } + + return &watch{ + watchedType: watchedType, + indexes: make(map[string]*index.Index), + mapper: mapper, + } +} + +func (w *watch) addIndex(index *index.Index) { + if _, indexExists := w.indexes[index.Name()]; indexExists { + panic(fmt.Sprintf("index with name %s is already defined", index.Name())) + } + + w.indexes[index.Name()] = index +} + +func (w *watch) key() string { + return resource.ToGVK(w.watchedType) +} diff --git a/internal/mesh/internal/controllers/explicitdestinations/controller.go b/internal/mesh/internal/controllers/explicitdestinations/controller.go index 41eb872896..58e2f615ad 100644 --- a/internal/mesh/internal/controllers/explicitdestinations/controller.go +++ b/internal/mesh/internal/controllers/explicitdestinations/controller.go @@ -12,6 +12,7 @@ import ( "google.golang.org/protobuf/types/known/anypb" "github.com/hashicorp/consul/internal/controller" + "github.com/hashicorp/consul/internal/controller/dependency" "github.com/hashicorp/consul/internal/mesh/internal/controllers/explicitdestinations/mapper" "github.com/hashicorp/consul/internal/mesh/internal/types" "github.com/hashicorp/consul/internal/resource" @@ -22,14 +23,14 @@ import ( const ControllerName = "consul.io/explicit-mapper-controller" -func Controller(mapper *mapper.Mapper) controller.Controller { +func Controller(mapper *mapper.Mapper) *controller.Controller { if mapper == nil { panic("mapper is required") } - return controller.ForType(pbmesh.ComputedExplicitDestinationsType). + return controller.NewController(ControllerName, pbmesh.ComputedExplicitDestinationsType). WithWatch(pbmesh.DestinationsType, mapper.MapDestinations). - WithWatch(pbcatalog.WorkloadType, controller.ReplaceType(pbmesh.ComputedExplicitDestinationsType)). + WithWatch(pbcatalog.WorkloadType, dependency.ReplaceType(pbmesh.ComputedExplicitDestinationsType)). WithWatch(pbcatalog.ServiceType, mapper.MapService). WithWatch(pbmesh.ComputedRoutesType, mapper.MapComputedRoute). WithReconciler(&reconciler{mapper: mapper}) diff --git a/internal/mesh/internal/controllers/meshconfiguration/controller.go b/internal/mesh/internal/controllers/meshconfiguration/controller.go index 93c0cb0d5f..de44340099 100644 --- a/internal/mesh/internal/controllers/meshconfiguration/controller.go +++ b/internal/mesh/internal/controllers/meshconfiguration/controller.go @@ -11,11 +11,15 @@ import ( pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v2beta1" ) +const ( + ControllerName = "consul.io/mesh-configuration" +) + // Controller instantiates a new Controller for managing MeshConfiguration resources. -func Controller() controller.Controller { +func Controller() *controller.Controller { r := &reconciler{} - return controller.ForType(pbmesh.MeshConfigurationType).WithReconciler(r) + return controller.NewController(ControllerName, pbmesh.MeshConfigurationType).WithReconciler(r) } // reconciler implements the Reconciler interface to modify runtime state based diff --git a/internal/mesh/internal/controllers/meshgateways/controller.go b/internal/mesh/internal/controllers/meshgateways/controller.go index 76cd44748c..e5de2d55e1 100644 --- a/internal/mesh/internal/controllers/meshgateways/controller.go +++ b/internal/mesh/internal/controllers/meshgateways/controller.go @@ -11,10 +11,14 @@ import ( pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v2beta1" ) -func Controller() controller.Controller { +const ( + ControllerName = "consul.io/mesh-gateway" +) + +func Controller() *controller.Controller { r := &reconciler{} - return controller.ForType(pbmesh.MeshGatewayType). + return controller.NewController(ControllerName, pbmesh.MeshGatewayType). WithReconciler(r) } diff --git a/internal/mesh/internal/controllers/proxyconfiguration/controller.go b/internal/mesh/internal/controllers/proxyconfiguration/controller.go index e0ac5d42ad..a99daf10c7 100644 --- a/internal/mesh/internal/controllers/proxyconfiguration/controller.go +++ b/internal/mesh/internal/controllers/proxyconfiguration/controller.go @@ -11,6 +11,7 @@ import ( "google.golang.org/protobuf/types/known/anypb" "github.com/hashicorp/consul/internal/controller" + "github.com/hashicorp/consul/internal/controller/dependency" "github.com/hashicorp/consul/internal/mesh/internal/mappers/workloadselectionmapper" "github.com/hashicorp/consul/internal/mesh/internal/types" "github.com/hashicorp/consul/internal/resource" @@ -21,14 +22,14 @@ import ( const ControllerName = "consul.io/proxy-configuration-controller" -func Controller(proxyConfigMapper *workloadselectionmapper.Mapper[*pbmesh.ProxyConfiguration]) controller.Controller { +func Controller(proxyConfigMapper *workloadselectionmapper.Mapper[*pbmesh.ProxyConfiguration]) *controller.Controller { if proxyConfigMapper == nil { panic("proxy config mapper is required") } - return controller.ForType(pbmesh.ComputedProxyConfigurationType). + return controller.NewController(ControllerName, pbmesh.ComputedProxyConfigurationType). WithWatch(pbmesh.ProxyConfigurationType, proxyConfigMapper.MapToComputedType). - WithWatch(pbcatalog.WorkloadType, controller.ReplaceType(pbmesh.ComputedProxyConfigurationType)). + WithWatch(pbcatalog.WorkloadType, dependency.ReplaceType(pbmesh.ComputedProxyConfigurationType)). WithReconciler(&reconciler{proxyConfigMapper: proxyConfigMapper}) } diff --git a/internal/mesh/internal/controllers/routes/controller.go b/internal/mesh/internal/controllers/routes/controller.go index e55bac45cc..9a9fabc076 100644 --- a/internal/mesh/internal/controllers/routes/controller.go +++ b/internal/mesh/internal/controllers/routes/controller.go @@ -20,13 +20,13 @@ import ( "github.com/hashicorp/consul/proto-public/pbresource" ) -func Controller() controller.Controller { +func Controller() *controller.Controller { mapper := xroutemapper.New() r := &routesReconciler{ mapper: mapper, } - return controller.ForType(pbmesh.ComputedRoutesType). + return controller.NewController(StatusKey, pbmesh.ComputedRoutesType). WithWatch(pbmesh.HTTPRouteType, mapper.MapHTTPRoute). WithWatch(pbmesh.GRPCRouteType, mapper.MapGRPCRoute). WithWatch(pbmesh.TCPRouteType, mapper.MapTCPRoute). diff --git a/internal/mesh/internal/controllers/sidecarproxy/controller.go b/internal/mesh/internal/controllers/sidecarproxy/controller.go index 6b81b04b8c..733b811081 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/controller.go +++ b/internal/mesh/internal/controllers/sidecarproxy/controller.go @@ -11,6 +11,7 @@ import ( "google.golang.org/protobuf/types/known/anypb" "github.com/hashicorp/consul/internal/controller" + "github.com/hashicorp/consul/internal/controller/dependency" "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" @@ -32,7 +33,7 @@ func Controller( trustDomainFetcher TrustDomainFetcher, dc string, defaultAllow bool, -) controller.Controller { +) *controller.Controller { if cache == nil || trustDomainFetcher == nil { panic("cache and trust domain fetcher are required") } @@ -84,11 +85,11 @@ func Controller( ComputedTrafficPermissions: find workloads in cache stored for this CTP=Workload, workloads=>PST reconcile requests */ - return controller.ForType(pbmesh.ProxyStateTemplateType). + return controller.NewController(ControllerName, pbmesh.ProxyStateTemplateType). WithWatch(pbcatalog.ServiceType, cache.MapService). - WithWatch(pbcatalog.WorkloadType, controller.ReplaceType(pbmesh.ProxyStateTemplateType)). - WithWatch(pbmesh.ComputedExplicitDestinationsType, controller.ReplaceType(pbmesh.ProxyStateTemplateType)). - WithWatch(pbmesh.ComputedProxyConfigurationType, controller.ReplaceType(pbmesh.ProxyStateTemplateType)). + WithWatch(pbcatalog.WorkloadType, dependency.ReplaceType(pbmesh.ProxyStateTemplateType)). + WithWatch(pbmesh.ComputedExplicitDestinationsType, dependency.ReplaceType(pbmesh.ProxyStateTemplateType)). + WithWatch(pbmesh.ComputedProxyConfigurationType, dependency.ReplaceType(pbmesh.ProxyStateTemplateType)). WithWatch(pbmesh.ComputedRoutesType, cache.MapComputedRoutes). WithWatch(pbauth.ComputedTrafficPermissionsType, cache.MapComputedTrafficPermissions). WithReconciler(&reconciler{ diff --git a/internal/mesh/internal/controllers/xds/controller.go b/internal/mesh/internal/controllers/xds/controller.go index 678cfd0539..19b35dca93 100644 --- a/internal/mesh/internal/controllers/xds/controller.go +++ b/internal/mesh/internal/controllers/xds/controller.go @@ -29,13 +29,13 @@ const ControllerName = "consul.io/xds-controller" const defaultTenancy = "default" -func Controller(endpointsMapper *bimapper.Mapper, updater ProxyUpdater, fetcher TrustBundleFetcher, leafCertManager *leafcert.Manager, leafMapper *LeafMapper, leafCancels *LeafCancels, datacenter string) controller.Controller { +func Controller(endpointsMapper *bimapper.Mapper, updater ProxyUpdater, fetcher TrustBundleFetcher, leafCertManager *leafcert.Manager, leafMapper *LeafMapper, leafCancels *LeafCancels, datacenter string) *controller.Controller { leafCertEvents := make(chan controller.Event, 1000) if endpointsMapper == nil || fetcher == nil || leafCertManager == nil || leafMapper == nil || datacenter == "" { panic("endpointsMapper, updater, fetcher, leafCertManager, leafMapper, and datacenter are required") } - return controller.ForType(pbmesh.ProxyStateTemplateType). + return controller.NewController(ControllerName, pbmesh.ProxyStateTemplateType). WithWatch(pbcatalog.ServiceEndpointsType, endpointsMapper.MapLink). WithCustomWatch(proxySource(updater), proxyMapper). WithCustomWatch(&controller.Source{Source: leafCertEvents}, leafMapper.EventMapLink). diff --git a/internal/multicluster/internal/controllers/exportedservices/controller.go b/internal/multicluster/internal/controllers/exportedservices/controller.go index ba7e42f36c..908daca7f4 100644 --- a/internal/multicluster/internal/controllers/exportedservices/controller.go +++ b/internal/multicluster/internal/controllers/exportedservices/controller.go @@ -20,9 +20,13 @@ import ( "github.com/hashicorp/consul/proto-public/pbresource" ) -func Controller() controller.Controller { +const ( + ControllerName = "consul.io/exported-services" +) - return controller.ForType(pbmulticluster.ComputedExportedServicesType). +func Controller() *controller.Controller { + + return controller.NewController(ControllerName, pbmulticluster.ComputedExportedServicesType). WithWatch(pbmulticluster.ExportedServicesType, ReplaceTypeForComputedExportedServices()). WithWatch(pbcatalog.ServiceType, ReplaceTypeForComputedExportedServices()). WithWatch(pbmulticluster.NamespaceExportedServicesType, ReplaceTypeForComputedExportedServices()). diff --git a/internal/resource/demo/controller.go b/internal/resource/demo/controller.go index a8757fcae2..b67da4782c 100644 --- a/internal/resource/demo/controller.go +++ b/internal/resource/demo/controller.go @@ -14,6 +14,7 @@ import ( "google.golang.org/protobuf/proto" "github.com/hashicorp/consul/internal/controller" + "github.com/hashicorp/consul/internal/controller/dependency" "github.com/hashicorp/consul/internal/resource" "github.com/hashicorp/consul/proto-public/pbresource" pbdemov2 "github.com/hashicorp/consul/proto/private/pbdemo/v2" @@ -27,9 +28,9 @@ func RegisterControllers(mgr *controller.Manager) { mgr.Register(artistController()) } -func artistController() controller.Controller { - return controller.ForType(TypeV2Artist). - WithWatch(TypeV2Album, controller.MapOwner). +func artistController() *controller.Controller { + return controller.NewController("artists", TypeV2Artist). + WithWatch(TypeV2Album, dependency.MapOwner). WithReconciler(&artistReconciler{}) } diff --git a/internal/resource/protoc-gen-resource-types/internal/generate/generate.go b/internal/resource/protoc-gen-resource-types/internal/generate/generate.go index 3d74077da9..0e1b6ddc29 100644 --- a/internal/resource/protoc-gen-resource-types/internal/generate/generate.go +++ b/internal/resource/protoc-gen-resource-types/internal/generate/generate.go @@ -68,7 +68,7 @@ func (g *generator) addResourceKindsFromFile(f *protogen.File) error { continue } - gvkString := strings.TrimPrefix(string(m.Desc.FullName()), "hashicorp.consul.") + gvkString := strings.TrimPrefix(strings.TrimPrefix(string(m.Desc.FullName()), "hashicorp.consul."), "internal.") rtype, err := resource.ParseGVK(gvkString) if err != nil { return err @@ -113,7 +113,7 @@ func (g *generator) ensureAPIGroup(gv apiGroupVersion, importPath protogen.GoImp func (g *generator) generateTypes(gp *protogen.Plugin) error { for _, info := range g.resources { - f := gp.NewGeneratedFile(filepath.Join(info.Directory, "resource_types.gen.go"), info.ImportPath) + f := gp.NewGeneratedFile(filepath.Join(info.Directory, "resources.rtypes.go"), info.ImportPath) sort.Slice(info.Kinds, func(a, b int) bool { return info.Kinds[a].Name < info.Kinds[b].Name diff --git a/internal/resource/reaper/controller.go b/internal/resource/reaper/controller.go index c79dde5551..27337c7b20 100644 --- a/internal/resource/reaper/controller.go +++ b/internal/resource/reaper/controller.go @@ -26,8 +26,8 @@ func RegisterControllers(mgr *controller.Manager) { mgr.Register(reaperController()) } -func reaperController() controller.Controller { - return controller.ForType(resource.TypeV1Tombstone). +func reaperController() *controller.Controller { + return controller.NewController(statusKeyReaperController, resource.TypeV1Tombstone). WithReconciler(newReconciler()) } diff --git a/internal/resource/resourcetest/builder.go b/internal/resource/resourcetest/builder.go index 423bd66aaa..ee37206b46 100644 --- a/internal/resource/resourcetest/builder.go +++ b/internal/resource/resourcetest/builder.go @@ -56,6 +56,11 @@ func (b *resourceBuilder) WithTenancy(tenant *pbresource.Tenancy) *resourceBuild return b } +func (b *resourceBuilder) WithVersion(version string) *resourceBuilder { + b.resource.Version = version + return b +} + func (b *resourceBuilder) WithData(t T, data protoreflect.ProtoMessage) *resourceBuilder { t.Helper() diff --git a/internal/tenancy/internal/controllers/namespace/controller.go b/internal/tenancy/internal/controllers/namespace/controller.go index 0d7a73e135..22a3e19e40 100644 --- a/internal/tenancy/internal/controllers/namespace/controller.go +++ b/internal/tenancy/internal/controllers/namespace/controller.go @@ -24,8 +24,8 @@ const ( // common.go for the full list. ) -func Controller(registry resource.Registry) controller.Controller { - return controller.ForType(pbtenancy.NamespaceType). +func Controller(registry resource.Registry) *controller.Controller { + return controller.NewController(StatusKey, pbtenancy.NamespaceType). WithReconciler(&Reconciler{Registry: registry}) } diff --git a/internal/testing/errors/errors.go b/internal/testing/errors/errors.go new file mode 100644 index 0000000000..c56d927c18 --- /dev/null +++ b/internal/testing/errors/errors.go @@ -0,0 +1,41 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package errors + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/hashicorp/consul/internal/testing/golden" +) + +func goldenError(t *testing.T, name string, actual string) { + t.Helper() + + expected := golden.Get(t, actual, name+".golden") + require.Equal(t, expected, actual) +} + +func TestErrorStrings(t *testing.T, cases map[string]error) { + for name, err := range cases { + t.Run(name, func(t *testing.T) { + goldenError(t, name, err.Error()) + }) + } +} + +type UnwrapErrorTestCase struct { + Err error + Expected error +} + +func TestErrorUnwrap(t *testing.T, cases map[string]UnwrapErrorTestCase) { + for name, tcase := range cases { + t.Run(name, func(t *testing.T) { + require.Equal(t, tcase.Expected, errors.Unwrap(tcase.Err)) + }) + } +} diff --git a/proto-public/go.mod b/proto-public/go.mod index 8ac41fa940..c1512e4f83 100644 --- a/proto-public/go.mod +++ b/proto-public/go.mod @@ -20,7 +20,6 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/stretchr/objx v0.5.0 // indirect golang.org/x/net v0.17.0 // indirect golang.org/x/sys v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect diff --git a/proto-public/go.sum b/proto-public/go.sum index 135da88aac..51de68d8d6 100644 --- a/proto-public/go.sum +++ b/proto-public/go.sum @@ -31,12 +31,7 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -89,7 +84,6 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/api v0.26.2 h1:dM3cinp3PGB6asOySalOZxEG4CZ0IAdJsrYZXE/ovGQ= diff --git a/proto-public/pbauth/v2beta1/resource_types.gen.go b/proto-public/pbauth/v2beta1/resources.rtypes.go similarity index 100% rename from proto-public/pbauth/v2beta1/resource_types.gen.go rename to proto-public/pbauth/v2beta1/resources.rtypes.go diff --git a/proto-public/pbcatalog/v2beta1/resource_types.gen.go b/proto-public/pbcatalog/v2beta1/resources.rtypes.go similarity index 100% rename from proto-public/pbcatalog/v2beta1/resource_types.gen.go rename to proto-public/pbcatalog/v2beta1/resources.rtypes.go diff --git a/proto-public/pbdns/mock_DNSServiceClient.go b/proto-public/pbdns/mock_DNSServiceClient.go deleted file mode 100644 index d9fffda65a..0000000000 --- a/proto-public/pbdns/mock_DNSServiceClient.go +++ /dev/null @@ -1,64 +0,0 @@ -// Code generated by mockery v2.20.0. DO NOT EDIT. - -package pbdns - -import ( - context "context" - - grpc "google.golang.org/grpc" - - mock "github.com/stretchr/testify/mock" -) - -// MockDNSServiceClient is an autogenerated mock type for the DNSServiceClient type -type MockDNSServiceClient struct { - mock.Mock -} - -// Query provides a mock function with given fields: ctx, in, opts -func (_m *MockDNSServiceClient) Query(ctx context.Context, in *QueryRequest, opts ...grpc.CallOption) (*QueryResponse, error) { - _va := make([]interface{}, len(opts)) - for _i := range opts { - _va[_i] = opts[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, in) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *QueryResponse - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *QueryRequest, ...grpc.CallOption) (*QueryResponse, error)); ok { - return rf(ctx, in, opts...) - } - if rf, ok := ret.Get(0).(func(context.Context, *QueryRequest, ...grpc.CallOption) *QueryResponse); ok { - r0 = rf(ctx, in, opts...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*QueryResponse) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *QueryRequest, ...grpc.CallOption) error); ok { - r1 = rf(ctx, in, opts...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -type mockConstructorTestingTNewMockDNSServiceClient interface { - mock.TestingT - Cleanup(func()) -} - -// NewMockDNSServiceClient creates a new instance of MockDNSServiceClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewMockDNSServiceClient(t mockConstructorTestingTNewMockDNSServiceClient) *MockDNSServiceClient { - mock := &MockDNSServiceClient{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/proto-public/pbdns/mock_DNSServiceServer.go b/proto-public/pbdns/mock_DNSServiceServer.go deleted file mode 100644 index e78c7d4c30..0000000000 --- a/proto-public/pbdns/mock_DNSServiceServer.go +++ /dev/null @@ -1,55 +0,0 @@ -// Code generated by mockery v2.20.0. DO NOT EDIT. - -package pbdns - -import ( - context "context" - - mock "github.com/stretchr/testify/mock" -) - -// MockDNSServiceServer is an autogenerated mock type for the DNSServiceServer type -type MockDNSServiceServer struct { - mock.Mock -} - -// Query provides a mock function with given fields: _a0, _a1 -func (_m *MockDNSServiceServer) Query(_a0 context.Context, _a1 *QueryRequest) (*QueryResponse, error) { - ret := _m.Called(_a0, _a1) - - var r0 *QueryResponse - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *QueryRequest) (*QueryResponse, error)); ok { - return rf(_a0, _a1) - } - if rf, ok := ret.Get(0).(func(context.Context, *QueryRequest) *QueryResponse); ok { - r0 = rf(_a0, _a1) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*QueryResponse) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *QueryRequest) error); ok { - r1 = rf(_a0, _a1) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -type mockConstructorTestingTNewMockDNSServiceServer interface { - mock.TestingT - Cleanup(func()) -} - -// NewMockDNSServiceServer creates a new instance of MockDNSServiceServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewMockDNSServiceServer(t mockConstructorTestingTNewMockDNSServiceServer) *MockDNSServiceServer { - mock := &MockDNSServiceServer{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/proto-public/pbdns/mock_UnsafeDNSServiceServer.go b/proto-public/pbdns/mock_UnsafeDNSServiceServer.go deleted file mode 100644 index 43a9e1e461..0000000000 --- a/proto-public/pbdns/mock_UnsafeDNSServiceServer.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated by mockery v2.20.0. DO NOT EDIT. - -package pbdns - -import mock "github.com/stretchr/testify/mock" - -// MockUnsafeDNSServiceServer is an autogenerated mock type for the UnsafeDNSServiceServer type -type MockUnsafeDNSServiceServer struct { - mock.Mock -} - -// mustEmbedUnimplementedDNSServiceServer provides a mock function with given fields: -func (_m *MockUnsafeDNSServiceServer) mustEmbedUnimplementedDNSServiceServer() { - _m.Called() -} - -type mockConstructorTestingTNewMockUnsafeDNSServiceServer interface { - mock.TestingT - Cleanup(func()) -} - -// NewMockUnsafeDNSServiceServer creates a new instance of MockUnsafeDNSServiceServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewMockUnsafeDNSServiceServer(t mockConstructorTestingTNewMockUnsafeDNSServiceServer) *MockUnsafeDNSServiceServer { - mock := &MockUnsafeDNSServiceServer{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/proto-public/pbmesh/v2beta1/resource_types.gen.go b/proto-public/pbmesh/v2beta1/resources.rtypes.go similarity index 100% rename from proto-public/pbmesh/v2beta1/resource_types.gen.go rename to proto-public/pbmesh/v2beta1/resources.rtypes.go diff --git a/proto-public/pbmulticluster/v2beta1/resource_types.gen.go b/proto-public/pbmulticluster/v2beta1/resources.rtypes.go similarity index 100% rename from proto-public/pbmulticluster/v2beta1/resource_types.gen.go rename to proto-public/pbmulticluster/v2beta1/resources.rtypes.go diff --git a/proto-public/pbtenancy/v2beta1/resource_types.gen.go b/proto-public/pbtenancy/v2beta1/resources.rtypes.go similarity index 100% rename from proto-public/pbtenancy/v2beta1/resource_types.gen.go rename to proto-public/pbtenancy/v2beta1/resources.rtypes.go diff --git a/proto/buf.gen.yaml b/proto/buf.gen.yaml index efb9703b10..4f599505ba 100644 --- a/proto/buf.gen.yaml +++ b/proto/buf.gen.yaml @@ -26,3 +26,8 @@ plugins: out: . opt: - paths=source_relative + - name: resource-types + out: . + opt: + - paths=source_relative + strategy: all diff --git a/proto/private/pbdemo/v1/demo.pb.go b/proto/private/pbdemo/v1/demo.pb.go index 2cbf9ea8d1..ff71ae24f4 100644 --- a/proto/private/pbdemo/v1/demo.pb.go +++ b/proto/private/pbdemo/v1/demo.pb.go @@ -13,6 +13,7 @@ package demov1 import ( + _ "github.com/hashicorp/consul/proto-public/pbresource" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" @@ -153,7 +154,7 @@ func (x *Executive) GetPosition() string { return "" } -// Partition scoped resource. +// Partition scoped resource type RecordLabel struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -397,66 +398,71 @@ var file_private_pbdemo_v1_demo_proto_rawDesc = []byte{ 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x65, 0x6d, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x21, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x64, 0x65, 0x6d, 0x6f, 0x2e, 0x76, - 0x31, 0x22, 0x27, 0x0a, 0x09, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x76, 0x65, 0x12, 0x1a, - 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x43, 0x0a, 0x0b, 0x52, 0x65, - 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, - 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, - 0xa3, 0x01, 0x0a, 0x06, 0x41, 0x72, 0x74, 0x69, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, - 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x3e, 0x0a, 0x05, 0x67, 0x65, 0x6e, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x28, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, - 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x64, 0x65, 0x6d, 0x6f, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x72, 0x65, 0x52, 0x05, 0x67, 0x65, 0x6e, 0x72, 0x65, - 0x12, 0x23, 0x0a, 0x0d, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, - 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, - 0x6d, 0x62, 0x65, 0x72, 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x05, 0x41, 0x6c, 0x62, 0x75, 0x6d, 0x12, + 0x31, 0x1a, 0x1c, 0x70, 0x62, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2f, 0x61, 0x6e, + 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, + 0x2f, 0x0a, 0x09, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x76, 0x65, 0x12, 0x1a, 0x0a, 0x08, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x06, 0xa2, 0x93, 0x04, 0x02, 0x08, 0x01, + 0x22, 0x4b, 0x0a, 0x0b, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x79, 0x65, 0x61, 0x72, 0x5f, 0x6f, 0x66, 0x5f, 0x72, - 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x79, 0x65, - 0x61, 0x72, 0x4f, 0x66, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x14, 0x63, - 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x6c, 0x79, 0x5f, 0x61, 0x63, 0x63, 0x6c, 0x61, 0x69, - 0x6d, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x63, 0x72, 0x69, 0x74, 0x69, - 0x63, 0x61, 0x6c, 0x6c, 0x79, 0x41, 0x63, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x65, 0x64, 0x12, 0x16, - 0x0a, 0x06, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, - 0x74, 0x72, 0x61, 0x63, 0x6b, 0x73, 0x22, 0x09, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x63, 0x65, 0x70, - 0x74, 0x2a, 0xe9, 0x01, 0x0a, 0x05, 0x47, 0x65, 0x6e, 0x72, 0x65, 0x12, 0x15, 0x0a, 0x11, 0x47, - 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, - 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x4a, 0x41, 0x5a, 0x5a, - 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x46, 0x4f, 0x4c, 0x4b, - 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x50, 0x4f, 0x50, 0x10, - 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, - 0x10, 0x04, 0x12, 0x0e, 0x0a, 0x0a, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x50, 0x55, 0x4e, 0x4b, - 0x10, 0x05, 0x12, 0x0f, 0x0a, 0x0b, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x42, 0x4c, 0x55, 0x45, - 0x53, 0x10, 0x06, 0x12, 0x11, 0x0a, 0x0d, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x52, 0x5f, 0x41, - 0x4e, 0x44, 0x5f, 0x42, 0x10, 0x07, 0x12, 0x11, 0x0a, 0x0d, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, - 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x52, 0x59, 0x10, 0x08, 0x12, 0x0f, 0x0a, 0x0b, 0x47, 0x45, 0x4e, - 0x52, 0x45, 0x5f, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x10, 0x09, 0x12, 0x0d, 0x0a, 0x09, 0x47, 0x45, - 0x4e, 0x52, 0x45, 0x5f, 0x53, 0x4b, 0x41, 0x10, 0x0a, 0x12, 0x11, 0x0a, 0x0d, 0x47, 0x45, 0x4e, - 0x52, 0x45, 0x5f, 0x48, 0x49, 0x50, 0x5f, 0x48, 0x4f, 0x50, 0x10, 0x0b, 0x12, 0x0f, 0x0a, 0x0b, - 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x44, 0x49, 0x45, 0x10, 0x0c, 0x42, 0x97, 0x02, - 0x0a, 0x25, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, - 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, - 0x64, 0x65, 0x6d, 0x6f, 0x2e, 0x76, 0x31, 0x42, 0x09, 0x44, 0x65, 0x6d, 0x6f, 0x50, 0x72, 0x6f, - 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x75, - 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x2f, - 0x70, 0x62, 0x64, 0x65, 0x6d, 0x6f, 0x2f, 0x76, 0x31, 0x3b, 0x64, 0x65, 0x6d, 0x6f, 0x76, 0x31, - 0xa2, 0x02, 0x04, 0x48, 0x43, 0x49, 0x44, 0xaa, 0x02, 0x21, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, - 0x6f, 0x72, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x2e, 0x44, 0x65, 0x6d, 0x6f, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x21, 0x48, 0x61, - 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x49, - 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5c, 0x44, 0x65, 0x6d, 0x6f, 0x5c, 0x56, 0x31, 0xe2, - 0x02, 0x2d, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, - 0x75, 0x6c, 0x5c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5c, 0x44, 0x65, 0x6d, 0x6f, - 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, - 0x02, 0x25, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, - 0x73, 0x75, 0x6c, 0x3a, 0x3a, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x3a, 0x3a, 0x44, - 0x65, 0x6d, 0x6f, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x06, 0xa2, 0x93, 0x04, 0x02, 0x08, 0x02, 0x22, 0xab, 0x01, + 0x0a, 0x06, 0x41, 0x72, 0x74, 0x69, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, + 0x0a, 0x05, 0x67, 0x65, 0x6e, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28, 0x2e, + 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, + 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x64, 0x65, 0x6d, 0x6f, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x72, 0x65, 0x52, 0x05, 0x67, 0x65, 0x6e, 0x72, 0x65, 0x12, 0x23, + 0x0a, 0x0d, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, + 0x65, 0x72, 0x73, 0x3a, 0x06, 0xa2, 0x93, 0x04, 0x02, 0x08, 0x03, 0x22, 0x96, 0x01, 0x0a, 0x05, + 0x41, 0x6c, 0x62, 0x75, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x79, 0x65, 0x61, + 0x72, 0x5f, 0x6f, 0x66, 0x5f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x0d, 0x79, 0x65, 0x61, 0x72, 0x4f, 0x66, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, + 0x65, 0x12, 0x31, 0x0a, 0x14, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x6c, 0x79, 0x5f, + 0x61, 0x63, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x13, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x6c, 0x79, 0x41, 0x63, 0x63, 0x6c, 0x61, + 0x69, 0x6d, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x73, 0x18, 0x04, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x73, 0x3a, 0x06, 0xa2, 0x93, + 0x04, 0x02, 0x08, 0x03, 0x22, 0x11, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x63, 0x65, 0x70, 0x74, 0x3a, + 0x06, 0xa2, 0x93, 0x04, 0x02, 0x08, 0x03, 0x2a, 0xe9, 0x01, 0x0a, 0x05, 0x47, 0x65, 0x6e, 0x72, + 0x65, 0x12, 0x15, 0x0a, 0x11, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, + 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x47, 0x45, 0x4e, 0x52, + 0x45, 0x5f, 0x4a, 0x41, 0x5a, 0x5a, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x47, 0x45, 0x4e, 0x52, + 0x45, 0x5f, 0x46, 0x4f, 0x4c, 0x4b, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x47, 0x45, 0x4e, 0x52, + 0x45, 0x5f, 0x50, 0x4f, 0x50, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x47, 0x45, 0x4e, 0x52, 0x45, + 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x10, 0x04, 0x12, 0x0e, 0x0a, 0x0a, 0x47, 0x45, 0x4e, 0x52, + 0x45, 0x5f, 0x50, 0x55, 0x4e, 0x4b, 0x10, 0x05, 0x12, 0x0f, 0x0a, 0x0b, 0x47, 0x45, 0x4e, 0x52, + 0x45, 0x5f, 0x42, 0x4c, 0x55, 0x45, 0x53, 0x10, 0x06, 0x12, 0x11, 0x0a, 0x0d, 0x47, 0x45, 0x4e, + 0x52, 0x45, 0x5f, 0x52, 0x5f, 0x41, 0x4e, 0x44, 0x5f, 0x42, 0x10, 0x07, 0x12, 0x11, 0x0a, 0x0d, + 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x52, 0x59, 0x10, 0x08, 0x12, + 0x0f, 0x0a, 0x0b, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x10, 0x09, + 0x12, 0x0d, 0x0a, 0x09, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x53, 0x4b, 0x41, 0x10, 0x0a, 0x12, + 0x11, 0x0a, 0x0d, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x48, 0x49, 0x50, 0x5f, 0x48, 0x4f, 0x50, + 0x10, 0x0b, 0x12, 0x0f, 0x0a, 0x0b, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x44, 0x49, + 0x45, 0x10, 0x0c, 0x42, 0x97, 0x02, 0x0a, 0x25, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x61, 0x73, 0x68, + 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x64, 0x65, 0x6d, 0x6f, 0x2e, 0x76, 0x31, 0x42, 0x09, 0x44, + 0x65, 0x6d, 0x6f, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, + 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, + 0x69, 0x76, 0x61, 0x74, 0x65, 0x2f, 0x70, 0x62, 0x64, 0x65, 0x6d, 0x6f, 0x2f, 0x76, 0x31, 0x3b, + 0x64, 0x65, 0x6d, 0x6f, 0x76, 0x31, 0xa2, 0x02, 0x04, 0x48, 0x43, 0x49, 0x44, 0xaa, 0x02, 0x21, + 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, + 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x44, 0x65, 0x6d, 0x6f, 0x2e, 0x56, + 0x31, 0xca, 0x02, 0x21, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, + 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5c, 0x44, 0x65, + 0x6d, 0x6f, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x2d, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, + 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x5c, 0x44, 0x65, 0x6d, 0x6f, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x25, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, + 0x70, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x3a, 0x3a, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x3a, 0x3a, 0x44, 0x65, 0x6d, 0x6f, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/proto/private/pbdemo/v1/demo.proto b/proto/private/pbdemo/v1/demo.proto index be2f393aaf..effe61fb28 100644 --- a/proto/private/pbdemo/v1/demo.proto +++ b/proto/private/pbdemo/v1/demo.proto @@ -7,19 +7,27 @@ syntax = "proto3"; // Consul's generic storage APIs. package hashicorp.consul.internal.demo.v1; +import "pbresource/annotations.proto"; + // Cluster scoped resource. message Executive { + option (hashicorp.consul.resource.spec) = {scope: SCOPE_CLUSTER}; + string position = 1; } -// Partition scoped resource. +// Partition scoped resource message RecordLabel { + option (hashicorp.consul.resource.spec) = {scope: SCOPE_PARTITION}; + string name = 1; string description = 2; } // Namespace scoped resource. message Artist { + option (hashicorp.consul.resource.spec) = {scope: SCOPE_NAMESPACE}; + string name = 1; string description = 2; Genre genre = 3; @@ -43,10 +51,14 @@ enum Genre { } message Album { + option (hashicorp.consul.resource.spec) = {scope: SCOPE_NAMESPACE}; + string name = 1; int32 year_of_release = 2; bool critically_acclaimed = 3; repeated string tracks = 4; } -message Concept {} +message Concept { + option (hashicorp.consul.resource.spec) = {scope: SCOPE_NAMESPACE}; +} diff --git a/proto/private/pbdemo/v1/resources.rtypes.go b/proto/private/pbdemo/v1/resources.rtypes.go new file mode 100644 index 0000000000..b6832318d2 --- /dev/null +++ b/proto/private/pbdemo/v1/resources.rtypes.go @@ -0,0 +1,50 @@ +// Code generated by protoc-gen-resource-types. DO NOT EDIT. + +package demov1 + +import ( + "github.com/hashicorp/consul/proto-public/pbresource" +) + +const ( + GroupName = "demo" + Version = "v1" + + AlbumKind = "Album" + ArtistKind = "Artist" + ConceptKind = "Concept" + ExecutiveKind = "Executive" + RecordLabelKind = "RecordLabel" +) + +var ( + AlbumType = &pbresource.Type{ + Group: GroupName, + GroupVersion: Version, + Kind: AlbumKind, + } + + ArtistType = &pbresource.Type{ + Group: GroupName, + GroupVersion: Version, + Kind: ArtistKind, + } + + ConceptType = &pbresource.Type{ + Group: GroupName, + GroupVersion: Version, + Kind: ConceptKind, + } + + ExecutiveType = &pbresource.Type{ + Group: GroupName, + GroupVersion: Version, + Kind: ExecutiveKind, + } + + RecordLabelType = &pbresource.Type{ + Group: GroupName, + GroupVersion: Version, + Kind: RecordLabelKind, + } +) diff --git a/proto/private/pbdemo/v2/demo.pb.go b/proto/private/pbdemo/v2/demo.pb.go index 9d9a7053a7..6ae9207250 100644 --- a/proto/private/pbdemo/v2/demo.pb.go +++ b/proto/private/pbdemo/v2/demo.pb.go @@ -13,6 +13,7 @@ package demov2 import ( + _ "github.com/hashicorp/consul/proto-public/pbresource" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" @@ -246,65 +247,67 @@ var file_private_pbdemo_v2_demo_proto_rawDesc = []byte{ 0x2f, 0x76, 0x32, 0x2f, 0x64, 0x65, 0x6d, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x21, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x64, 0x65, 0x6d, 0x6f, 0x2e, 0x76, - 0x32, 0x22, 0xff, 0x01, 0x0a, 0x06, 0x41, 0x72, 0x74, 0x69, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x3e, 0x0a, 0x05, 0x67, 0x65, 0x6e, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x28, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, - 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x64, 0x65, 0x6d, 0x6f, - 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x6e, 0x72, 0x65, 0x52, 0x05, 0x67, 0x65, 0x6e, 0x72, 0x65, - 0x12, 0x60, 0x0a, 0x0d, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, - 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, - 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x2e, 0x64, 0x65, 0x6d, 0x6f, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x72, 0x74, 0x69, - 0x73, 0x74, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0c, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, - 0x72, 0x73, 0x1a, 0x3f, 0x0a, 0x11, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, - 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x22, 0x8e, 0x01, 0x0a, 0x05, 0x41, 0x6c, 0x62, 0x75, 0x6d, 0x12, 0x14, 0x0a, - 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, - 0x74, 0x6c, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x79, 0x65, 0x61, 0x72, 0x5f, 0x6f, 0x66, 0x5f, 0x72, - 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x79, 0x65, - 0x61, 0x72, 0x4f, 0x66, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x13, 0x63, - 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x6c, 0x79, 0x5f, 0x61, 0x63, 0x6c, 0x61, 0x69, 0x6d, - 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, - 0x61, 0x6c, 0x6c, 0x79, 0x41, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, - 0x74, 0x72, 0x61, 0x63, 0x6b, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x74, 0x72, - 0x61, 0x63, 0x6b, 0x73, 0x2a, 0xe9, 0x01, 0x0a, 0x05, 0x47, 0x65, 0x6e, 0x72, 0x65, 0x12, 0x15, - 0x0a, 0x11, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, - 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x4a, - 0x41, 0x5a, 0x5a, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x46, - 0x4f, 0x4c, 0x4b, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x50, - 0x4f, 0x50, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x4d, 0x45, - 0x54, 0x41, 0x4c, 0x10, 0x04, 0x12, 0x0e, 0x0a, 0x0a, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x50, - 0x55, 0x4e, 0x4b, 0x10, 0x05, 0x12, 0x0f, 0x0a, 0x0b, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x42, - 0x4c, 0x55, 0x45, 0x53, 0x10, 0x06, 0x12, 0x11, 0x0a, 0x0d, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, - 0x52, 0x5f, 0x41, 0x4e, 0x44, 0x5f, 0x42, 0x10, 0x07, 0x12, 0x11, 0x0a, 0x0d, 0x47, 0x45, 0x4e, - 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x52, 0x59, 0x10, 0x08, 0x12, 0x0f, 0x0a, 0x0b, - 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x10, 0x09, 0x12, 0x0d, 0x0a, - 0x09, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x53, 0x4b, 0x41, 0x10, 0x0a, 0x12, 0x11, 0x0a, 0x0d, - 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x48, 0x49, 0x50, 0x5f, 0x48, 0x4f, 0x50, 0x10, 0x0b, 0x12, - 0x0f, 0x0a, 0x0b, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x44, 0x49, 0x45, 0x10, 0x0c, - 0x42, 0x97, 0x02, 0x0a, 0x25, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, - 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x2e, 0x64, 0x65, 0x6d, 0x6f, 0x2e, 0x76, 0x32, 0x42, 0x09, 0x44, 0x65, 0x6d, 0x6f, - 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x63, 0x6f, - 0x6e, 0x73, 0x75, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x69, 0x76, 0x61, - 0x74, 0x65, 0x2f, 0x70, 0x62, 0x64, 0x65, 0x6d, 0x6f, 0x2f, 0x76, 0x32, 0x3b, 0x64, 0x65, 0x6d, - 0x6f, 0x76, 0x32, 0xa2, 0x02, 0x04, 0x48, 0x43, 0x49, 0x44, 0xaa, 0x02, 0x21, 0x48, 0x61, 0x73, - 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x44, 0x65, 0x6d, 0x6f, 0x2e, 0x56, 0x32, 0xca, 0x02, - 0x21, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, - 0x6c, 0x5c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5c, 0x44, 0x65, 0x6d, 0x6f, 0x5c, - 0x56, 0x32, 0xe2, 0x02, 0x2d, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, - 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5c, 0x44, - 0x65, 0x6d, 0x6f, 0x5c, 0x56, 0x32, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0xea, 0x02, 0x25, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x3a, 0x3a, - 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x3a, 0x3a, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x3a, 0x3a, 0x44, 0x65, 0x6d, 0x6f, 0x3a, 0x3a, 0x56, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x32, 0x1a, 0x1c, 0x70, 0x62, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2f, 0x61, 0x6e, + 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, + 0x87, 0x02, 0x0a, 0x06, 0x41, 0x72, 0x74, 0x69, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3e, + 0x0a, 0x05, 0x67, 0x65, 0x6e, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28, 0x2e, + 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, + 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x64, 0x65, 0x6d, 0x6f, 0x2e, 0x76, + 0x32, 0x2e, 0x47, 0x65, 0x6e, 0x72, 0x65, 0x52, 0x05, 0x67, 0x65, 0x6e, 0x72, 0x65, 0x12, 0x60, + 0x0a, 0x0d, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, + 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x2e, 0x64, 0x65, 0x6d, 0x6f, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x72, 0x74, 0x69, 0x73, 0x74, + 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x0c, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, + 0x1a, 0x3f, 0x0a, 0x11, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x3a, 0x06, 0xa2, 0x93, 0x04, 0x02, 0x08, 0x03, 0x22, 0x96, 0x01, 0x0a, 0x05, 0x41, 0x6c, + 0x62, 0x75, 0x6d, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x79, 0x65, 0x61, + 0x72, 0x5f, 0x6f, 0x66, 0x5f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x0d, 0x79, 0x65, 0x61, 0x72, 0x4f, 0x66, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, + 0x65, 0x12, 0x2f, 0x0a, 0x13, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x6c, 0x79, 0x5f, + 0x61, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, + 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x6c, 0x79, 0x41, 0x63, 0x6c, 0x61, 0x69, 0x6d, + 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x73, 0x18, 0x04, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x06, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x73, 0x3a, 0x06, 0xa2, 0x93, 0x04, 0x02, + 0x08, 0x03, 0x2a, 0xe9, 0x01, 0x0a, 0x05, 0x47, 0x65, 0x6e, 0x72, 0x65, 0x12, 0x15, 0x0a, 0x11, + 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, + 0x44, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x4a, 0x41, 0x5a, + 0x5a, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x46, 0x4f, 0x4c, + 0x4b, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x50, 0x4f, 0x50, + 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x4d, 0x45, 0x54, 0x41, + 0x4c, 0x10, 0x04, 0x12, 0x0e, 0x0a, 0x0a, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x50, 0x55, 0x4e, + 0x4b, 0x10, 0x05, 0x12, 0x0f, 0x0a, 0x0b, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x42, 0x4c, 0x55, + 0x45, 0x53, 0x10, 0x06, 0x12, 0x11, 0x0a, 0x0d, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x52, 0x5f, + 0x41, 0x4e, 0x44, 0x5f, 0x42, 0x10, 0x07, 0x12, 0x11, 0x0a, 0x0d, 0x47, 0x45, 0x4e, 0x52, 0x45, + 0x5f, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x52, 0x59, 0x10, 0x08, 0x12, 0x0f, 0x0a, 0x0b, 0x47, 0x45, + 0x4e, 0x52, 0x45, 0x5f, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x10, 0x09, 0x12, 0x0d, 0x0a, 0x09, 0x47, + 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x53, 0x4b, 0x41, 0x10, 0x0a, 0x12, 0x11, 0x0a, 0x0d, 0x47, 0x45, + 0x4e, 0x52, 0x45, 0x5f, 0x48, 0x49, 0x50, 0x5f, 0x48, 0x4f, 0x50, 0x10, 0x0b, 0x12, 0x0f, 0x0a, + 0x0b, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x44, 0x49, 0x45, 0x10, 0x0c, 0x42, 0x97, + 0x02, 0x0a, 0x25, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, + 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x2e, 0x64, 0x65, 0x6d, 0x6f, 0x2e, 0x76, 0x32, 0x42, 0x09, 0x44, 0x65, 0x6d, 0x6f, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x73, + 0x75, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, + 0x2f, 0x70, 0x62, 0x64, 0x65, 0x6d, 0x6f, 0x2f, 0x76, 0x32, 0x3b, 0x64, 0x65, 0x6d, 0x6f, 0x76, + 0x32, 0xa2, 0x02, 0x04, 0x48, 0x43, 0x49, 0x44, 0xaa, 0x02, 0x21, 0x48, 0x61, 0x73, 0x68, 0x69, + 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x44, 0x65, 0x6d, 0x6f, 0x2e, 0x56, 0x32, 0xca, 0x02, 0x21, 0x48, + 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5c, 0x44, 0x65, 0x6d, 0x6f, 0x5c, 0x56, 0x32, + 0xe2, 0x02, 0x2d, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, + 0x73, 0x75, 0x6c, 0x5c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5c, 0x44, 0x65, 0x6d, + 0x6f, 0x5c, 0x56, 0x32, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0xea, 0x02, 0x25, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x3a, 0x3a, 0x43, 0x6f, + 0x6e, 0x73, 0x75, 0x6c, 0x3a, 0x3a, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x3a, 0x3a, + 0x44, 0x65, 0x6d, 0x6f, 0x3a, 0x3a, 0x56, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/proto/private/pbdemo/v2/demo.proto b/proto/private/pbdemo/v2/demo.proto index 546cfe6972..b0c9d9bb70 100644 --- a/proto/private/pbdemo/v2/demo.proto +++ b/proto/private/pbdemo/v2/demo.proto @@ -7,7 +7,11 @@ syntax = "proto3"; // Consul's generic storage APIs. package hashicorp.consul.internal.demo.v2; +import "pbresource/annotations.proto"; + message Artist { + option (hashicorp.consul.resource.spec) = {scope: SCOPE_NAMESPACE}; + string name = 1; Genre genre = 2; map group_members = 3; @@ -30,6 +34,8 @@ enum Genre { } message Album { + option (hashicorp.consul.resource.spec) = {scope: SCOPE_NAMESPACE}; + string title = 1; int32 year_of_release = 2; bool critically_aclaimed = 3; diff --git a/proto/private/pbdemo/v2/resources.rtypes.go b/proto/private/pbdemo/v2/resources.rtypes.go new file mode 100644 index 0000000000..ffe2ecc082 --- /dev/null +++ b/proto/private/pbdemo/v2/resources.rtypes.go @@ -0,0 +1,29 @@ +// Code generated by protoc-gen-resource-types. DO NOT EDIT. + +package demov2 + +import ( + "github.com/hashicorp/consul/proto-public/pbresource" +) + +const ( + GroupName = "demo" + Version = "v2" + + AlbumKind = "Album" + ArtistKind = "Artist" +) + +var ( + AlbumType = &pbresource.Type{ + Group: GroupName, + GroupVersion: Version, + Kind: AlbumKind, + } + + ArtistType = &pbresource.Type{ + Group: GroupName, + GroupVersion: Version, + Kind: ArtistKind, + } +) diff --git a/test/integration/consul-container/go.mod b/test/integration/consul-container/go.mod index 357a588ee0..20690dcf5a 100644 --- a/test/integration/consul-container/go.mod +++ b/test/integration/consul-container/go.mod @@ -74,12 +74,14 @@ require ( github.com/hashicorp/go-bexpr v0.1.2 // indirect github.com/hashicorp/go-hclog v1.5.0 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-immutable-radix/v2 v2.1.0 // indirect github.com/hashicorp/go-msgpack v1.1.5 // indirect github.com/hashicorp/go-netaddrs v0.1.0 // indirect github.com/hashicorp/go-retryablehttp v0.6.7 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-sockaddr v1.0.2 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.0 // indirect github.com/hashicorp/memberlist v0.5.0 // indirect github.com/hashicorp/raft v1.5.0 // indirect github.com/hashicorp/raft-autopilot v0.1.6 // indirect diff --git a/test/integration/consul-container/go.sum b/test/integration/consul-container/go.sum index 04fcd47386..7c8f604c3c 100644 --- a/test/integration/consul-container/go.sum +++ b/test/integration/consul-container/go.sum @@ -248,6 +248,8 @@ github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVH github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix/v2 v2.1.0 h1:CUW5RYIcysz+D3B+l1mDeXrQ7fUvGGCwJfdASSzbrfo= +github.com/hashicorp/go-immutable-radix/v2 v2.1.0/go.mod h1:hgdqLXA4f6NIjRVisM1TJ9aOJVNRqKZj+xDGF6m7PBw= github.com/hashicorp/go-memdb v1.3.4 h1:XSL3NR682X/cVk2IeV0d70N4DZ9ljI885xAEU8IoK3c= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= @@ -279,6 +281,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru/v2 v2.0.0 h1:Lf+9eD8m5pncvHAOCQj49GSN6aQI8XGfI5OpXNkoWaA= +github.com/hashicorp/golang-lru/v2 v2.0.0/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hil v0.0.0-20200423225030-a18a1cd20038 h1:n9J0rwVWXDpNd5iZnwY7w4WZyq53/rROeI7OVvLW8Ok=