mirror of https://github.com/status-im/consul.git
Tenancy Bridge v2 (#19220)
* tenancy bridge v2 for v2 resources * add missing copywrite headers
This commit is contained in:
parent
b962d91056
commit
d5c9f11b59
|
@ -8,6 +8,7 @@ import (
|
|||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/hashicorp/consul/internal/tenancy"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
|
@ -1456,7 +1457,8 @@ func (s *Server) setupExternalGRPC(config *Config, deps Deps, logger hclog.Logge
|
|||
|
||||
tenancyBridge := NewV1TenancyBridge(s)
|
||||
if stringslice.Contains(deps.Experiments, V2TenancyExperimentName) {
|
||||
tenancyBridge = resource.NewV2TenancyBridge()
|
||||
tenancyBridgeV2 := tenancy.NewV2TenancyBridge()
|
||||
tenancyBridge = tenancyBridgeV2.WithClient(s.insecureResourceServiceClient)
|
||||
}
|
||||
|
||||
s.resourceServiceServer = resourcegrpc.NewServer(resourcegrpc.Config{
|
||||
|
@ -1477,8 +1479,9 @@ func (s *Server) setupInsecureResourceServiceClient(typeRegistry resource.Regist
|
|||
}
|
||||
|
||||
tenancyBridge := NewV1TenancyBridge(s)
|
||||
tenancyBridgeV2 := tenancy.NewV2TenancyBridge()
|
||||
if stringslice.Contains(deps.Experiments, V2TenancyExperimentName) {
|
||||
tenancyBridge = resource.NewV2TenancyBridge()
|
||||
tenancyBridge = tenancyBridgeV2
|
||||
}
|
||||
server := resourcegrpc.NewServer(resourcegrpc.Config{
|
||||
Registry: typeRegistry,
|
||||
|
@ -1493,7 +1496,7 @@ func (s *Server) setupInsecureResourceServiceClient(typeRegistry resource.Regist
|
|||
return err
|
||||
}
|
||||
s.insecureResourceServiceClient = pbresource.NewResourceServiceClient(conn)
|
||||
|
||||
tenancyBridgeV2.WithClient(s.insecureResourceServiceClient)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -42,8 +42,8 @@ func (s *Server) ListByOwner(ctx context.Context, req *pbresource.ListByOwnerReq
|
|||
return nil, status.Errorf(codes.Internal, "failed list acl: %v", err)
|
||||
}
|
||||
|
||||
// Check v1 tenancy exists for the v2 resource.
|
||||
if err = v1TenancyExists(reg, s.TenancyBridge, req.Owner.Tenancy, codes.InvalidArgument); err != nil {
|
||||
// Check tenancy exists for the v2 resource.
|
||||
if err = tenancyExists(reg, s.TenancyBridge, req.Owner.Tenancy, codes.InvalidArgument); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
|
@ -59,8 +59,8 @@ func (s *Server) Read(ctx context.Context, req *pbresource.ReadRequest) (*pbreso
|
|||
return nil, status.Errorf(codes.Internal, "failed read acl: %v", err)
|
||||
}
|
||||
|
||||
// Check V1 tenancy exists for the V2 resource.
|
||||
if err = v1TenancyExists(reg, s.TenancyBridge, req.Id.Tenancy, codes.NotFound); err != nil {
|
||||
// Check tenancy exists for the V2 resource.
|
||||
if err = tenancyExists(reg, s.TenancyBridge, req.Id.Tenancy, codes.NotFound); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
|
@ -208,10 +208,10 @@ func validateWildcardTenancy(tenancy *pbresource.Tenancy, namePrefix string) err
|
|||
return nil
|
||||
}
|
||||
|
||||
// v1TenancyExists return an error with the passed in gRPC status code when tenancy partition or namespace do not exist.
|
||||
func v1TenancyExists(reg *resource.Registration, v1Bridge TenancyBridge, tenancy *pbresource.Tenancy, errCode codes.Code) error {
|
||||
// tenancyExists return an error with the passed in gRPC status code when tenancy partition or namespace do not exist.
|
||||
func tenancyExists(reg *resource.Registration, tenancyBridge TenancyBridge, tenancy *pbresource.Tenancy, errCode codes.Code) error {
|
||||
if reg.Scope == resource.ScopePartition || reg.Scope == resource.ScopeNamespace {
|
||||
exists, err := v1Bridge.PartitionExists(tenancy.Partition)
|
||||
exists, err := tenancyBridge.PartitionExists(tenancy.Partition)
|
||||
switch {
|
||||
case err != nil:
|
||||
return err
|
||||
|
@ -221,7 +221,7 @@ func v1TenancyExists(reg *resource.Registration, v1Bridge TenancyBridge, tenancy
|
|||
}
|
||||
|
||||
if reg.Scope == resource.ScopeNamespace {
|
||||
exists, err := v1Bridge.NamespaceExists(tenancy.Partition, tenancy.Namespace)
|
||||
exists, err := tenancyBridge.NamespaceExists(tenancy.Partition, tenancy.Namespace)
|
||||
switch {
|
||||
case err != nil:
|
||||
return err
|
||||
|
@ -232,10 +232,10 @@ func v1TenancyExists(reg *resource.Registration, v1Bridge TenancyBridge, tenancy
|
|||
return nil
|
||||
}
|
||||
|
||||
// v1TenancyMarkedForDeletion returns a gRPC InvalidArgument when either partition or namespace is marked for deletion.
|
||||
func v1TenancyMarkedForDeletion(reg *resource.Registration, v1Bridge TenancyBridge, tenancy *pbresource.Tenancy) error {
|
||||
// tenancyMarkedForDeletion returns a gRPC InvalidArgument when either partition or namespace is marked for deletion.
|
||||
func tenancyMarkedForDeletion(reg *resource.Registration, tenancyBridge TenancyBridge, tenancy *pbresource.Tenancy) error {
|
||||
if reg.Scope == resource.ScopePartition || reg.Scope == resource.ScopeNamespace {
|
||||
marked, err := v1Bridge.IsPartitionMarkedForDeletion(tenancy.Partition)
|
||||
marked, err := tenancyBridge.IsPartitionMarkedForDeletion(tenancy.Partition)
|
||||
switch {
|
||||
case err != nil:
|
||||
return err
|
||||
|
@ -245,7 +245,7 @@ func v1TenancyMarkedForDeletion(reg *resource.Registration, v1Bridge TenancyBrid
|
|||
}
|
||||
|
||||
if reg.Scope == resource.ScopeNamespace {
|
||||
marked, err := v1Bridge.IsNamespaceMarkedForDeletion(tenancy.Partition, tenancy.Namespace)
|
||||
marked, err := tenancyBridge.IsNamespaceMarkedForDeletion(tenancy.Partition, tenancy.Namespace)
|
||||
switch {
|
||||
case err != nil:
|
||||
return err
|
||||
|
|
|
@ -75,7 +75,7 @@ func testServer(t *testing.T) *Server {
|
|||
}
|
||||
})
|
||||
|
||||
// Mock the V1 tenancy bridge since we can't use the real thing.
|
||||
// Mock the tenancy bridge since we can't use the real thing.
|
||||
mockTenancyBridge := &MockTenancyBridge{}
|
||||
mockTenancyBridge.On("PartitionExists", resource.DefaultPartitionName).Return(true, nil)
|
||||
mockTenancyBridge.On("NamespaceExists", resource.DefaultPartitionName, resource.DefaultNamespaceName).Return(true, nil)
|
||||
|
|
|
@ -5,14 +5,13 @@ package testing
|
|||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/consul/internal/tenancy"
|
||||
"github.com/hashicorp/go-uuid"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
|
||||
"github.com/hashicorp/go-uuid"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/consul/acl"
|
||||
"github.com/hashicorp/consul/acl/resolver"
|
||||
|
@ -98,6 +97,12 @@ func RunResourceServiceWithConfig(t *testing.T, config svc.Config, registerFns .
|
|||
mockTenancyBridge.On("IsPartitionMarkedForDeletion", "foo").Return(false, nil)
|
||||
mockTenancyBridge.On("IsNamespaceMarkedForDeletion", resource.DefaultPartitionName, resource.DefaultNamespaceName).Return(false, nil)
|
||||
config.TenancyBridge = mockTenancyBridge
|
||||
} else {
|
||||
switch config.TenancyBridge.(type) {
|
||||
case *tenancy.V2TenancyBridge:
|
||||
err = initTenancy(ctx, backend)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
if config.ACLResolver == nil {
|
||||
|
@ -140,6 +145,14 @@ func RunResourceServiceWithConfig(t *testing.T, config svc.Config, registerFns .
|
|||
)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() { _ = conn.Close() })
|
||||
client := pbresource.NewResourceServiceClient(conn)
|
||||
if config.TenancyBridge != nil {
|
||||
switch config.TenancyBridge.(type) {
|
||||
case *tenancy.V2TenancyBridge:
|
||||
config.TenancyBridge.(*tenancy.V2TenancyBridge).WithClient(client)
|
||||
}
|
||||
|
||||
return pbresource.NewResourceServiceClient(conn)
|
||||
}
|
||||
|
||||
return client
|
||||
}
|
||||
|
|
|
@ -6,7 +6,17 @@
|
|||
package testing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/hashicorp/consul/acl"
|
||||
"github.com/hashicorp/consul/internal/resource"
|
||||
"github.com/hashicorp/consul/internal/storage"
|
||||
"github.com/hashicorp/consul/internal/storage/inmem"
|
||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||
pbtenancy "github.com/hashicorp/consul/proto-public/pbtenancy/v2beta1"
|
||||
"github.com/oklog/ulid/v2"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
"time"
|
||||
)
|
||||
|
||||
func FillEntMeta(entMeta *acl.EnterpriseMeta) {
|
||||
|
@ -16,3 +26,38 @@ func FillEntMeta(entMeta *acl.EnterpriseMeta) {
|
|||
func FillAuthorizerContext(authzContext *acl.AuthorizerContext) {
|
||||
// nothing to to in CE.
|
||||
}
|
||||
|
||||
// initTenancy create the base tenancy objects (default/default)
|
||||
func initTenancy(ctx context.Context, b *inmem.Backend) error {
|
||||
//TODO(dhiaayachi): This is now called for testing purpose but at some point we need to add something similar
|
||||
// when bootstrapping a server, probably in the tenancy controllers.
|
||||
nsData, err := anypb.New(&pbtenancy.Namespace{Description: "default namespace in default partition"})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nsID := &pbresource.ID{
|
||||
Type: pbtenancy.NamespaceType,
|
||||
Name: resource.DefaultNamespaceName,
|
||||
Tenancy: resource.DefaultPartitionedTenancy(),
|
||||
Uid: ulid.Make().String(),
|
||||
}
|
||||
read, err := b.Read(ctx, storage.StrongConsistency, nsID)
|
||||
if err != nil && !errors.Is(err, storage.ErrNotFound) {
|
||||
return err
|
||||
}
|
||||
if read == nil && errors.Is(err, storage.ErrNotFound) {
|
||||
_, err = b.WriteCAS(ctx, &pbresource.Resource{
|
||||
Id: nsID,
|
||||
Generation: ulid.Make().String(),
|
||||
Data: nsData,
|
||||
Metadata: map[string]string{
|
||||
"generated_at": time.Now().Format(time.RFC3339),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
|
|
@ -78,13 +78,13 @@ func (s *Server) Write(ctx context.Context, req *pbresource.WriteRequest) (*pbre
|
|||
return nil, status.Errorf(codes.Internal, "failed write acl: %v", err)
|
||||
}
|
||||
|
||||
// Check V1 tenancy exists for the V2 resource
|
||||
if err = v1TenancyExists(reg, s.TenancyBridge, req.Resource.Id.Tenancy, codes.InvalidArgument); err != nil {
|
||||
// Check tenancy exists for the V2 resource
|
||||
if err = tenancyExists(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.TenancyBridge, req.Resource.Id.Tenancy); err != nil {
|
||||
// Check tenancy not marked for deletion.
|
||||
if err = tenancyMarkedForDeletion(reg, s.TenancyBridge, req.Resource.Id.Tenancy); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
|
@ -34,9 +34,9 @@ func (s *Server) WriteStatus(ctx context.Context, req *pbresource.WriteStatusReq
|
|||
// Apply defaults when tenancy units empty.
|
||||
v1EntMetaToV2Tenancy(reg, entMeta, req.Id.Tenancy)
|
||||
|
||||
// Check V1 tenancy exists for the V2 resource. Ignore "marked for deletion" since status updates
|
||||
// Check tenancy exists for the V2 resource. Ignore "marked for deletion" since status updates
|
||||
// should still work regardless.
|
||||
if err = v1TenancyExists(reg, s.TenancyBridge, req.Id.Tenancy, codes.InvalidArgument); err != nil {
|
||||
if err = tenancyExists(reg, s.TenancyBridge, req.Id.Tenancy, codes.InvalidArgument); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
|
@ -157,7 +157,7 @@ func TestResourceWriteHandler(t *testing.T) {
|
|||
|
||||
require.Equal(t, http.StatusForbidden, rsp.Result().StatusCode)
|
||||
})
|
||||
|
||||
var readRsp *pbresource.ReadResponse
|
||||
t.Run("should write to the resource backend", func(t *testing.T) {
|
||||
rsp := httptest.NewRecorder()
|
||||
req := httptest.NewRequest("PUT", "/demo/v2/artist/keith-urban?partition=default&peer_name=local&namespace=default", strings.NewReader(`
|
||||
|
@ -183,7 +183,8 @@ func TestResourceWriteHandler(t *testing.T) {
|
|||
require.Equal(t, "Keith Urban", result["data"].(map[string]any)["name"])
|
||||
require.Equal(t, "keith-urban", result["id"].(map[string]any)["name"])
|
||||
|
||||
readRsp, err := client.Read(testutil.TestContext(t), &pbresource.ReadRequest{
|
||||
var err error
|
||||
readRsp, err = client.Read(testutil.TestContext(t), &pbresource.ReadRequest{
|
||||
Id: &pbresource.ID{
|
||||
Type: demo.TypeV2Artist,
|
||||
Tenancy: resource.DefaultNamespacedTenancy(),
|
||||
|
@ -200,7 +201,7 @@ func TestResourceWriteHandler(t *testing.T) {
|
|||
|
||||
t.Run("should update the record with version parameter", func(t *testing.T) {
|
||||
rsp := httptest.NewRecorder()
|
||||
req := httptest.NewRequest("PUT", "/demo/v2/artist/keith-urban?partition=default&peer_name=local&namespace=default&version=1", strings.NewReader(`
|
||||
req := httptest.NewRequest("PUT", fmt.Sprintf("/demo/v2/artist/keith-urban?partition=default&peer_name=local&namespace=default&version=%s", readRsp.Resource.Version), strings.NewReader(`
|
||||
{
|
||||
"metadata": {
|
||||
"foo": "bar"
|
||||
|
|
|
@ -24,15 +24,6 @@ const (
|
|||
DefaultPeerName = "local"
|
||||
)
|
||||
|
||||
// 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
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ package tenancy
|
|||
|
||||
import (
|
||||
"github.com/hashicorp/consul/internal/resource"
|
||||
"github.com/hashicorp/consul/internal/tenancy/internal/bridge"
|
||||
"github.com/hashicorp/consul/internal/tenancy/internal/types"
|
||||
)
|
||||
|
||||
|
@ -21,8 +22,16 @@ var (
|
|||
NamespaceV2Beta1Type = types.NamespaceV2Beta1Type
|
||||
)
|
||||
|
||||
type (
|
||||
V2TenancyBridge = bridge.V2TenancyBridge
|
||||
)
|
||||
|
||||
// RegisterTypes adds all resource types within the "tenancy" API group
|
||||
// to the given type registry
|
||||
func RegisterTypes(r resource.Registry) {
|
||||
types.Register(r)
|
||||
}
|
||||
|
||||
func NewV2TenancyBridge() *V2TenancyBridge {
|
||||
return bridge.NewV2TenancyBridge()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package bridge
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/hashicorp/consul/internal/tenancy/internal/types"
|
||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||
)
|
||||
|
||||
// V2TenancyBridge is used by the resource service to access V2 implementations of
|
||||
// partitions and namespaces.
|
||||
type V2TenancyBridge struct {
|
||||
client pbresource.ResourceServiceClient
|
||||
}
|
||||
|
||||
// WithClient inject a ResourceServiceClient in the V2TenancyBridge.
|
||||
// This is needed to break a circular dependency between
|
||||
// the ResourceServiceServer, ResourceServiceClient and the TenancyBridge
|
||||
func (b *V2TenancyBridge) WithClient(client pbresource.ResourceServiceClient) *V2TenancyBridge {
|
||||
b.client = client
|
||||
return b
|
||||
}
|
||||
|
||||
func NewV2TenancyBridge() *V2TenancyBridge {
|
||||
return &V2TenancyBridge{}
|
||||
}
|
||||
|
||||
func (b *V2TenancyBridge) NamespaceExists(partition, namespace string) (bool, error) {
|
||||
read, err := b.client.Read(context.Background(), &pbresource.ReadRequest{
|
||||
Id: &pbresource.ID{
|
||||
Name: namespace,
|
||||
Tenancy: &pbresource.Tenancy{
|
||||
Partition: partition,
|
||||
},
|
||||
Type: types.NamespaceType,
|
||||
},
|
||||
})
|
||||
return read != nil && read.Resource != nil, err
|
||||
}
|
||||
|
||||
func (b *V2TenancyBridge) IsNamespaceMarkedForDeletion(partition, namespace string) (bool, error) {
|
||||
read, err := b.client.Read(context.Background(), &pbresource.ReadRequest{
|
||||
Id: &pbresource.ID{
|
||||
Name: namespace,
|
||||
Tenancy: &pbresource.Tenancy{
|
||||
Partition: partition,
|
||||
},
|
||||
Type: types.NamespaceType,
|
||||
},
|
||||
})
|
||||
return read.Resource != nil, err
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
//go:build !consulent
|
||||
|
||||
package resource
|
||||
package bridge
|
||||
|
||||
func (b *V2TenancyBridge) PartitionExists(partition string) (bool, error) {
|
||||
if partition == "default" {
|
||||
|
@ -15,14 +15,3 @@ func (b *V2TenancyBridge) PartitionExists(partition string) (bool, error) {
|
|||
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
|
||||
}
|
|
@ -4,24 +4,15 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
|
||||
rtest "github.com/hashicorp/consul/internal/resource/resourcetest"
|
||||
"github.com/hashicorp/consul/internal/resource"
|
||||
pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v2beta1"
|
||||
"github.com/hashicorp/consul/proto/private/prototest"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||
pbtenancy "github.com/hashicorp/consul/proto-public/pbtenancy/v2beta1"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
|
||||
"github.com/hashicorp/consul/internal/resource"
|
||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||
pbtenancy "github.com/hashicorp/consul/proto-public/pbtenancy/v2beta1"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func createNamespaceResource(t *testing.T, data protoreflect.ProtoMessage) *pbresource.Resource {
|
||||
|
@ -144,51 +135,6 @@ func TestValidateNamespace(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestRead_Success(t *testing.T) {
|
||||
client := svctest.RunResourceService(t, Register)
|
||||
client = rtest.NewClient(client)
|
||||
|
||||
res := rtest.Resource(NamespaceType, "ns1").
|
||||
WithData(t, validNamespace()).
|
||||
Write(t, client)
|
||||
|
||||
readRsp, err := client.Read(context.Background(), &pbresource.ReadRequest{Id: res.Id})
|
||||
require.NoError(t, err)
|
||||
prototest.AssertDeepEqual(t, res.Id, readRsp.Resource.Id)
|
||||
}
|
||||
|
||||
func TestRead_NotFound(t *testing.T) {
|
||||
client := svctest.RunResourceService(t, Register)
|
||||
client = rtest.NewClient(client)
|
||||
|
||||
res := rtest.Resource(NamespaceType, "ns1").
|
||||
WithData(t, validNamespace()).Build()
|
||||
|
||||
_, err := client.Read(context.Background(), &pbresource.ReadRequest{Id: res.Id})
|
||||
require.Error(t, err)
|
||||
require.Equal(t, codes.NotFound.String(), status.Code(err).String())
|
||||
}
|
||||
|
||||
func TestDelete_Success(t *testing.T) {
|
||||
client := svctest.RunResourceService(t, Register)
|
||||
client = rtest.NewClient(client)
|
||||
|
||||
res := rtest.Resource(NamespaceType, "ns1").
|
||||
WithData(t, validNamespace()).Write(t, client)
|
||||
|
||||
readRsp, err := client.Read(context.Background(), &pbresource.ReadRequest{Id: res.Id})
|
||||
require.NoError(t, err)
|
||||
prototest.AssertDeepEqual(t, res.Id, readRsp.Resource.Id)
|
||||
|
||||
_, err = client.Delete(context.Background(), &pbresource.DeleteRequest{Id: res.Id})
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = client.Read(context.Background(), &pbresource.ReadRequest{Id: res.Id})
|
||||
require.Error(t, err)
|
||||
require.Equal(t, codes.NotFound.String(), status.Code(err).String())
|
||||
|
||||
}
|
||||
|
||||
func validNamespace() *pbtenancy.Namespace {
|
||||
return &pbtenancy.Namespace{
|
||||
Description: "ns namespace",
|
|
@ -0,0 +1,107 @@
|
|||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package tenancytest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/hashicorp/consul/agent/grpc-external/services/resource"
|
||||
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
|
||||
resource2 "github.com/hashicorp/consul/internal/resource"
|
||||
"github.com/hashicorp/consul/internal/tenancy"
|
||||
"testing"
|
||||
|
||||
rtest "github.com/hashicorp/consul/internal/resource/resourcetest"
|
||||
"github.com/hashicorp/consul/proto/private/prototest"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||
pbtenancy "github.com/hashicorp/consul/proto-public/pbtenancy/v2beta1"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestReadNamespace_Success(t *testing.T) {
|
||||
v2TenancyBridge := tenancy.NewV2TenancyBridge()
|
||||
config := resource.Config{TenancyBridge: v2TenancyBridge}
|
||||
client := svctest.RunResourceServiceWithConfig(t, config, tenancy.RegisterTypes)
|
||||
cl := rtest.NewClient(client)
|
||||
|
||||
res := rtest.Resource(pbtenancy.NamespaceType, "ns1").
|
||||
WithData(t, validNamespace()).
|
||||
Write(t, cl)
|
||||
|
||||
readRsp, err := cl.Read(context.Background(), &pbresource.ReadRequest{Id: res.Id})
|
||||
require.NoError(t, err)
|
||||
prototest.AssertDeepEqual(t, res.Id, readRsp.Resource.Id)
|
||||
}
|
||||
|
||||
func TestReadNamespace_NotFound(t *testing.T) {
|
||||
v2TenancyBridge := tenancy.NewV2TenancyBridge()
|
||||
config := resource.Config{TenancyBridge: v2TenancyBridge}
|
||||
client := svctest.RunResourceServiceWithConfig(t, config, tenancy.RegisterTypes)
|
||||
cl := rtest.NewClient(client)
|
||||
|
||||
res := rtest.Resource(pbtenancy.NamespaceType, "ns1").
|
||||
WithData(t, validNamespace()).Build()
|
||||
|
||||
_, err := cl.Read(context.Background(), &pbresource.ReadRequest{Id: res.Id})
|
||||
require.Error(t, err)
|
||||
require.Equal(t, codes.NotFound.String(), status.Code(err).String())
|
||||
}
|
||||
|
||||
func TestDeleteNamespace_Success(t *testing.T) {
|
||||
v2TenancyBridge := tenancy.NewV2TenancyBridge()
|
||||
config := resource.Config{TenancyBridge: v2TenancyBridge}
|
||||
client := svctest.RunResourceServiceWithConfig(t, config, tenancy.RegisterTypes)
|
||||
cl := rtest.NewClient(client)
|
||||
|
||||
res := rtest.Resource(pbtenancy.NamespaceType, "ns1").
|
||||
WithData(t, validNamespace()).Write(t, cl)
|
||||
|
||||
readRsp, err := cl.Read(context.Background(), &pbresource.ReadRequest{Id: res.Id})
|
||||
require.NoError(t, err)
|
||||
prototest.AssertDeepEqual(t, res.Id, readRsp.Resource.Id)
|
||||
|
||||
_, err = cl.Delete(context.Background(), &pbresource.DeleteRequest{Id: res.Id})
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = cl.Read(context.Background(), &pbresource.ReadRequest{Id: res.Id})
|
||||
require.Error(t, err)
|
||||
require.Equal(t, codes.NotFound.String(), status.Code(err).String())
|
||||
|
||||
}
|
||||
|
||||
func TestListNamespace_Success(t *testing.T) {
|
||||
v2TenancyBridge := tenancy.NewV2TenancyBridge()
|
||||
config := resource.Config{TenancyBridge: v2TenancyBridge}
|
||||
client := svctest.RunResourceServiceWithConfig(t, config, tenancy.RegisterTypes)
|
||||
cl := rtest.NewClient(client)
|
||||
|
||||
res := rtest.Resource(pbtenancy.NamespaceType, "ns1").
|
||||
WithData(t, validNamespace()).Write(t, cl)
|
||||
|
||||
require.NotNil(t, res)
|
||||
res = rtest.Resource(pbtenancy.NamespaceType, "ns2").
|
||||
WithData(t, validNamespace()).Write(t, cl)
|
||||
|
||||
require.NotNil(t, res)
|
||||
|
||||
listRsp, err := cl.List(context.Background(), &pbresource.ListRequest{Type: pbtenancy.NamespaceType, Tenancy: resource2.DefaultPartitionedTenancy()})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, listRsp.Resources, 3)
|
||||
names := []string{
|
||||
listRsp.Resources[0].Id.Name,
|
||||
listRsp.Resources[1].Id.Name,
|
||||
listRsp.Resources[2].Id.Name,
|
||||
}
|
||||
require.Contains(t, names, "default")
|
||||
require.Contains(t, names, "ns1")
|
||||
require.Contains(t, names, "ns2")
|
||||
}
|
||||
|
||||
func validNamespace() *pbtenancy.Namespace {
|
||||
return &pbtenancy.Namespace{
|
||||
Description: "ns namespace",
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue