From 4435e4a420d5f5f7e6d8726e65b62596554846fe Mon Sep 17 00:00:00 2001 From: Dhia Ayachi Date: Mon, 18 Sep 2023 12:25:05 -0400 Subject: [PATCH] add v2 tenancy bridge Flag and v2 Tenancy Bridge initial implementation (#18830) * add v2 tenancy bridge and a feature flag for v2 tenancy * move tenancy bridge v2 under resource package --- agent/consul/server.go | 34 ++++++++++++------- agent/consul/tenancy_bridge.go | 4 ++- .../grpc-external/services/resource/delete.go | 2 +- .../services/resource/list_by_owner.go | 2 +- agent/grpc-external/services/resource/read.go | 2 +- .../grpc-external/services/resource/server.go | 4 +-- .../services/resource/server_test.go | 10 +++--- .../services/resource/testing/testing.go | 10 +++--- .../grpc-external/services/resource/write.go | 4 +-- .../services/resource/write_status.go | 2 +- .../services/resource/write_test.go | 2 +- internal/resource/tenancy.go | 16 +++++++++ internal/resource/tenancy_bridge_ce.go | 29 ++++++++++++++++ 13 files changed, 89 insertions(+), 32 deletions(-) create mode 100644 internal/resource/tenancy_bridge_ce.go diff --git a/agent/consul/server.go b/agent/consul/server.go index c515f5443c..d2fd0472b9 100644 --- a/agent/consul/server.go +++ b/agent/consul/server.go @@ -137,6 +137,7 @@ const ( LeaderTransferMinVersion = "1.6.0" CatalogResourceExperimentName = "resource-apis" + V2TenancyExperimentName = "v2tenancy" ) const ( @@ -819,7 +820,7 @@ func NewServer(config *Config, flat Deps, externalGRPCServer *grpc.Server, go s.reportingManager.Run(&lib.StopChannelContext{StopCh: s.shutdownCh}) // Setup insecure resource service client. - if err := s.setupInsecureResourceServiceClient(flat.Registry, logger); err != nil { + if err := s.setupInsecureResourceServiceClient(flat.Registry, logger, flat); err != nil { return nil, err } @@ -1393,29 +1394,38 @@ func (s *Server) setupExternalGRPC(config *Config, deps Deps, logger hclog.Logge }) s.peerStreamServer.Register(s.externalGRPCServer) + tenancyBridge := NewV1TenancyBridge(s) + if stringslice.Contains(deps.Experiments, V2TenancyExperimentName) { + tenancyBridge = resource.NewV2TenancyBridge() + } + s.resourceServiceServer = resourcegrpc.NewServer(resourcegrpc.Config{ - Registry: deps.Registry, - Backend: s.raftStorageBackend, - ACLResolver: s.ACLResolver, - Logger: logger.Named("grpc-api.resource"), - V1TenancyBridge: NewV1TenancyBridge(s), + Registry: deps.Registry, + Backend: s.raftStorageBackend, + ACLResolver: s.ACLResolver, + Logger: logger.Named("grpc-api.resource"), + TenancyBridge: tenancyBridge, }) s.resourceServiceServer.Register(s.externalGRPCServer) reflection.Register(s.externalGRPCServer) } -func (s *Server) setupInsecureResourceServiceClient(typeRegistry resource.Registry, logger hclog.Logger) error { +func (s *Server) setupInsecureResourceServiceClient(typeRegistry resource.Registry, logger hclog.Logger, deps Deps) error { if s.raftStorageBackend == nil { return fmt.Errorf("raft storage backend cannot be nil") } + tenancyBridge := NewV1TenancyBridge(s) + if stringslice.Contains(deps.Experiments, V2TenancyExperimentName) { + tenancyBridge = resource.NewV2TenancyBridge() + } server := resourcegrpc.NewServer(resourcegrpc.Config{ - Registry: typeRegistry, - Backend: s.raftStorageBackend, - ACLResolver: resolver.DANGER_NO_AUTH{}, - Logger: logger.Named("grpc-api.resource"), - V1TenancyBridge: NewV1TenancyBridge(s), + Registry: typeRegistry, + Backend: s.raftStorageBackend, + ACLResolver: resolver.DANGER_NO_AUTH{}, + Logger: logger.Named("grpc-api.resource"), + TenancyBridge: tenancyBridge, }) conn, err := s.runInProcessGRPCServer(server.Register) diff --git a/agent/consul/tenancy_bridge.go b/agent/consul/tenancy_bridge.go index 4573db2feb..4e8daa0bc8 100644 --- a/agent/consul/tenancy_bridge.go +++ b/agent/consul/tenancy_bridge.go @@ -3,6 +3,8 @@ package consul +import "github.com/hashicorp/consul/agent/grpc-external/services/resource" + // V1TenancyBridge is used by the resource service to access V1 implementations of // partitions and namespaces. This bridge will be removed when V2 implemenations // of partitions and namespaces are available. @@ -10,6 +12,6 @@ type V1TenancyBridge struct { server *Server } -func NewV1TenancyBridge(server *Server) *V1TenancyBridge { +func NewV1TenancyBridge(server *Server) resource.TenancyBridge { return &V1TenancyBridge{server: server} } diff --git a/agent/grpc-external/services/resource/delete.go b/agent/grpc-external/services/resource/delete.go index 123ffac35b..2f30e27f98 100644 --- a/agent/grpc-external/services/resource/delete.go +++ b/agent/grpc-external/services/resource/delete.go @@ -20,7 +20,7 @@ import ( "github.com/hashicorp/consul/proto-public/pbresource" ) -// Deletes a resource. +// Delete deletes a resource. // - To delete a resource regardless of the stored version, set Version = "" // - Supports deleting a resource by name, hence Id.Uid may be empty. // - Delete of a previously deleted or non-existent resource is a no-op to support idempotency. diff --git a/agent/grpc-external/services/resource/list_by_owner.go b/agent/grpc-external/services/resource/list_by_owner.go index 3c0ccbda65..dd5b1dae85 100644 --- a/agent/grpc-external/services/resource/list_by_owner.go +++ b/agent/grpc-external/services/resource/list_by_owner.go @@ -43,7 +43,7 @@ func (s *Server) ListByOwner(ctx context.Context, req *pbresource.ListByOwnerReq } // Check v1 tenancy exists for the v2 resource. - if err = v1TenancyExists(reg, s.V1TenancyBridge, req.Owner.Tenancy, codes.InvalidArgument); err != nil { + if err = v1TenancyExists(reg, s.TenancyBridge, req.Owner.Tenancy, codes.InvalidArgument); err != nil { return nil, err } diff --git a/agent/grpc-external/services/resource/read.go b/agent/grpc-external/services/resource/read.go index febfcb69f0..fcf0e38aa8 100644 --- a/agent/grpc-external/services/resource/read.go +++ b/agent/grpc-external/services/resource/read.go @@ -54,7 +54,7 @@ func (s *Server) Read(ctx context.Context, req *pbresource.ReadRequest) (*pbreso } // Check V1 tenancy exists for the V2 resource. - if err = v1TenancyExists(reg, s.V1TenancyBridge, req.Id.Tenancy, codes.NotFound); err != nil { + if err = v1TenancyExists(reg, s.TenancyBridge, req.Id.Tenancy, codes.NotFound); err != nil { return nil, err } diff --git a/agent/grpc-external/services/resource/server.go b/agent/grpc-external/services/resource/server.go index 3ae839e69b..5fc5a01faf 100644 --- a/agent/grpc-external/services/resource/server.go +++ b/agent/grpc-external/services/resource/server.go @@ -31,9 +31,9 @@ type Config struct { // Backend is the storage backend that will be used for resource persistence. Backend Backend ACLResolver ACLResolver - // V1TenancyBridge temporarily allows us to use V1 implementations of + // TenancyBridge temporarily allows us to use V1 implementations of // partitions and namespaces until V2 implementations are available. - V1TenancyBridge TenancyBridge + TenancyBridge TenancyBridge } //go:generate mockery --name Registry --inpackage diff --git a/agent/grpc-external/services/resource/server_test.go b/agent/grpc-external/services/resource/server_test.go index 8f3967259a..99add64971 100644 --- a/agent/grpc-external/services/resource/server_test.go +++ b/agent/grpc-external/services/resource/server_test.go @@ -86,11 +86,11 @@ func testServer(t *testing.T) *Server { mockTenancyBridge.On("IsNamespaceMarkedForDeletion", resource.DefaultPartitionName, resource.DefaultNamespaceName).Return(false, nil) return NewServer(Config{ - Logger: testutil.Logger(t), - Registry: resource.NewRegistry(), - Backend: backend, - ACLResolver: mockACLResolver, - V1TenancyBridge: mockTenancyBridge, + Logger: testutil.Logger(t), + Registry: resource.NewRegistry(), + Backend: backend, + ACLResolver: mockACLResolver, + TenancyBridge: mockTenancyBridge, }) } diff --git a/agent/grpc-external/services/resource/testing/testing.go b/agent/grpc-external/services/resource/testing/testing.go index 4db4d6e586..ea06526fd1 100644 --- a/agent/grpc-external/services/resource/testing/testing.go +++ b/agent/grpc-external/services/resource/testing/testing.go @@ -101,11 +101,11 @@ func RunResourceServiceWithACL(t *testing.T, aclResolver svc.ACLResolver, regist mockTenancyBridge.On("IsNamespaceMarkedForDeletion", resource.DefaultPartitionName, resource.DefaultNamespaceName).Return(false, nil) svc.NewServer(svc.Config{ - Backend: backend, - Registry: registry, - Logger: testutil.Logger(t), - ACLResolver: aclResolver, - V1TenancyBridge: mockTenancyBridge, + Backend: backend, + Registry: registry, + Logger: testutil.Logger(t), + ACLResolver: aclResolver, + TenancyBridge: mockTenancyBridge, }).Register(server) pipe := internal.NewPipeListener() diff --git a/agent/grpc-external/services/resource/write.go b/agent/grpc-external/services/resource/write.go index eb66f355ff..2511e2f5da 100644 --- a/agent/grpc-external/services/resource/write.go +++ b/agent/grpc-external/services/resource/write.go @@ -71,12 +71,12 @@ func (s *Server) Write(ctx context.Context, req *pbresource.WriteRequest) (*pbre } // Check V1 tenancy exists for the V2 resource - if err = v1TenancyExists(reg, s.V1TenancyBridge, req.Resource.Id.Tenancy, codes.InvalidArgument); err != nil { + if err = v1TenancyExists(reg, s.TenancyBridge, req.Resource.Id.Tenancy, codes.InvalidArgument); err != nil { return nil, err } // Check V1 tenancy not marked for deletion. - if err = v1TenancyMarkedForDeletion(reg, s.V1TenancyBridge, req.Resource.Id.Tenancy); err != nil { + if err = v1TenancyMarkedForDeletion(reg, s.TenancyBridge, req.Resource.Id.Tenancy); err != nil { return nil, err } diff --git a/agent/grpc-external/services/resource/write_status.go b/agent/grpc-external/services/resource/write_status.go index 673f193da0..0d3b68bb08 100644 --- a/agent/grpc-external/services/resource/write_status.go +++ b/agent/grpc-external/services/resource/write_status.go @@ -36,7 +36,7 @@ func (s *Server) WriteStatus(ctx context.Context, req *pbresource.WriteStatusReq // Check V1 tenancy exists for the V2 resource. Ignore "marked for deletion" since status updates // should still work regardless. - if err = v1TenancyExists(reg, s.V1TenancyBridge, req.Id.Tenancy, codes.InvalidArgument); err != nil { + if err = v1TenancyExists(reg, s.TenancyBridge, req.Id.Tenancy, codes.InvalidArgument); err != nil { return nil, err } diff --git a/agent/grpc-external/services/resource/write_test.go b/agent/grpc-external/services/resource/write_test.go index 5a5e8d3180..3828ff9753 100644 --- a/agent/grpc-external/services/resource/write_test.go +++ b/agent/grpc-external/services/resource/write_test.go @@ -416,7 +416,7 @@ func TestWrite_Tenancy_MarkedForDeletion(t *testing.T) { mockTenancyBridge := &MockTenancyBridge{} mockTenancyBridge.On("PartitionExists", "part1").Return(true, nil) mockTenancyBridge.On("NamespaceExists", "part1", "ns1").Return(true, nil) - server.V1TenancyBridge = mockTenancyBridge + server.TenancyBridge = mockTenancyBridge _, err = client.Write(testContext(t), &pbresource.WriteRequest{Resource: tc.modFn(artist, recordLabel, mockTenancyBridge)}) require.Error(t, err) diff --git a/internal/resource/tenancy.go b/internal/resource/tenancy.go index 2a377ca2f5..c5e3d1b2b4 100644 --- a/internal/resource/tenancy.go +++ b/internal/resource/tenancy.go @@ -12,11 +12,27 @@ import ( "github.com/hashicorp/consul/proto-public/pbresource" ) +type TenancyBridge interface { + PartitionExists(partition string) (bool, error) + IsPartitionMarkedForDeletion(partition string) (bool, error) + NamespaceExists(partition, namespace string) (bool, error) + IsNamespaceMarkedForDeletion(partition, namespace string) (bool, error) +} + const ( DefaultPartitionName = "default" DefaultNamespaceName = "default" ) +// V2TenancyBridge is used by the resource service to access V2 implementations of +// partitions and namespaces. +type V2TenancyBridge struct { +} + +func NewV2TenancyBridge() TenancyBridge { + return &V2TenancyBridge{} +} + // Scope describes the tenancy scope of a resource. type Scope int diff --git a/internal/resource/tenancy_bridge_ce.go b/internal/resource/tenancy_bridge_ce.go new file mode 100644 index 0000000000..07be8ab508 --- /dev/null +++ b/internal/resource/tenancy_bridge_ce.go @@ -0,0 +1,29 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +//go:build !consulent +// +build !consulent + +package resource + +func (b *V2TenancyBridge) PartitionExists(partition string) (bool, error) { + if partition == "default" { + return true, nil + } + return false, nil +} + +func (b *V2TenancyBridge) IsPartitionMarkedForDeletion(partition string) (bool, error) { + return false, nil +} + +func (b *V2TenancyBridge) NamespaceExists(partition, namespace string) (bool, error) { + if partition == "default" && namespace == "default" { + return true, nil + } + return false, nil +} + +func (b *V2TenancyBridge) IsNamespaceMarkedForDeletion(partition, namespace string) (bool, error) { + return false, nil +}