mirror of https://github.com/hashicorp/consul
resource: block default namespace deletion + test refactorings (#19822)
parent
aca8a185ca
commit
c1bbda8128
|
@ -7,29 +7,29 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hashicorp/go-hclog"
|
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
"google.golang.org/protobuf/types/known/structpb"
|
"google.golang.org/protobuf/types/known/structpb"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-hclog"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/acl"
|
||||||
|
"github.com/hashicorp/consul/acl/resolver"
|
||||||
|
external "github.com/hashicorp/consul/agent/grpc-external"
|
||||||
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
|
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
|
||||||
|
"github.com/hashicorp/consul/agent/grpc-external/testutils"
|
||||||
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
"github.com/hashicorp/consul/internal/catalog"
|
"github.com/hashicorp/consul/internal/catalog"
|
||||||
"github.com/hashicorp/consul/internal/mesh"
|
"github.com/hashicorp/consul/internal/mesh"
|
||||||
"github.com/hashicorp/consul/internal/resource"
|
"github.com/hashicorp/consul/internal/resource"
|
||||||
"github.com/hashicorp/consul/internal/resource/resourcetest"
|
"github.com/hashicorp/consul/internal/resource/resourcetest"
|
||||||
pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v2beta1"
|
pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v2beta1"
|
||||||
|
"github.com/hashicorp/consul/proto-public/pbdataplane"
|
||||||
pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v2beta1"
|
pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v2beta1"
|
||||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||||
"github.com/hashicorp/consul/proto/private/prototest"
|
"github.com/hashicorp/consul/proto/private/prototest"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/acl"
|
|
||||||
"github.com/hashicorp/consul/acl/resolver"
|
|
||||||
external "github.com/hashicorp/consul/agent/grpc-external"
|
|
||||||
"github.com/hashicorp/consul/agent/grpc-external/testutils"
|
|
||||||
"github.com/hashicorp/consul/agent/structs"
|
|
||||||
"github.com/hashicorp/consul/proto-public/pbdataplane"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -262,7 +262,9 @@ func TestGetEnvoyBootstrapParams_Success_EnableV2(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
run := func(t *testing.T, tc testCase) {
|
run := func(t *testing.T, tc testCase) {
|
||||||
resourceClient := svctest.RunResourceService(t, catalog.RegisterTypes, mesh.RegisterTypes)
|
resourceClient := svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(catalog.RegisterTypes, mesh.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
|
|
||||||
options := structs.QueryOptions{Token: testToken}
|
options := structs.QueryOptions{Token: testToken}
|
||||||
ctx, err := external.ContextWithQueryOptions(context.Background(), options)
|
ctx, err := external.ContextWithQueryOptions(context.Background(), options)
|
||||||
|
@ -490,7 +492,9 @@ func TestGetEnvoyBootstrapParams_Error_EnableV2(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
run := func(t *testing.T, tc testCase) {
|
run := func(t *testing.T, tc testCase) {
|
||||||
resourceClient := svctest.RunResourceService(t, catalog.RegisterTypes, mesh.RegisterTypes)
|
resourceClient := svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(catalog.RegisterTypes, mesh.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
|
|
||||||
options := structs.QueryOptions{Token: testToken}
|
options := structs.QueryOptions{Token: testToken}
|
||||||
ctx, err := external.ContextWithQueryOptions(context.Background(), options)
|
ctx, err := external.ContextWithQueryOptions(context.Background(), options)
|
||||||
|
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"github.com/hashicorp/consul/internal/resource"
|
"github.com/hashicorp/consul/internal/resource"
|
||||||
"github.com/hashicorp/consul/internal/storage"
|
"github.com/hashicorp/consul/internal/storage"
|
||||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||||
|
pbtenancy "github.com/hashicorp/consul/proto-public/pbtenancy/v2beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Delete deletes a resource.
|
// Delete deletes a resource.
|
||||||
|
@ -188,16 +189,13 @@ func (s *Server) ensureDeleteRequestValid(req *pbresource.DeleteRequest) (*resou
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check scope
|
if err := validateScopedTenancy(reg.Scope, reg.Type, req.Id.Tenancy); err != nil {
|
||||||
if reg.Scope == resource.ScopePartition && req.Id.Tenancy.Namespace != "" {
|
return nil, err
|
||||||
return nil, status.Errorf(
|
|
||||||
codes.InvalidArgument,
|
|
||||||
"partition scoped resource %s cannot have a namespace. got: %s",
|
|
||||||
resource.ToGVK(req.Id.Type),
|
|
||||||
req.Id.Tenancy.Namespace,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := blockBuiltinsDeletion(reg.Type, req.Id); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return reg, nil
|
return reg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,3 +205,12 @@ func TombstoneNameFor(deleteId *pbresource.ID) string {
|
||||||
// deleteId.Name is just included for easier identification
|
// deleteId.Name is just included for easier identification
|
||||||
return fmt.Sprintf("tombstone-%v-%v", deleteId.Name, strings.ToLower(deleteId.Uid))
|
return fmt.Sprintf("tombstone-%v-%v", deleteId.Name, strings.ToLower(deleteId.Uid))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func blockDefaultNamespaceDeletion(rtype *pbresource.Type, id *pbresource.ID) error {
|
||||||
|
if id.Name == resource.DefaultNamespaceName &&
|
||||||
|
id.Tenancy.Partition == resource.DefaultPartitionName &&
|
||||||
|
resource.EqualType(rtype, pbtenancy.NamespaceType) {
|
||||||
|
return status.Errorf(codes.InvalidArgument, "cannot delete default namespace")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
// Copyright (c) HashiCorp, Inc.
|
||||||
|
// SPDX-License-Identifier: BUSL-1.1
|
||||||
|
|
||||||
|
//go:build !consulent
|
||||||
|
|
||||||
|
package resource
|
||||||
|
|
||||||
|
import "github.com/hashicorp/consul/proto-public/pbresource"
|
||||||
|
|
||||||
|
func blockBuiltinsDeletion(rtype *pbresource.Type, id *pbresource.ID) error {
|
||||||
|
if err := blockDefaultNamespaceDeletion(rtype, id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,10 +1,11 @@
|
||||||
// Copyright (c) HashiCorp, Inc.
|
// Copyright (c) HashiCorp, Inc.
|
||||||
// SPDX-License-Identifier: BUSL-1.1
|
// SPDX-License-Identifier: BUSL-1.1
|
||||||
|
|
||||||
package resource
|
package resource_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -15,123 +16,158 @@ import (
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/acl/resolver"
|
"github.com/hashicorp/consul/acl/resolver"
|
||||||
|
svc "github.com/hashicorp/consul/agent/grpc-external/services/resource"
|
||||||
|
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
|
||||||
"github.com/hashicorp/consul/internal/resource"
|
"github.com/hashicorp/consul/internal/resource"
|
||||||
"github.com/hashicorp/consul/internal/resource/demo"
|
"github.com/hashicorp/consul/internal/resource/demo"
|
||||||
rtest "github.com/hashicorp/consul/internal/resource/resourcetest"
|
rtest "github.com/hashicorp/consul/internal/resource/resourcetest"
|
||||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||||
|
pbtenancy "github.com/hashicorp/consul/proto-public/pbtenancy/v2beta1"
|
||||||
pbdemo "github.com/hashicorp/consul/proto/private/pbdemo/v1"
|
pbdemo "github.com/hashicorp/consul/proto/private/pbdemo/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDelete_InputValidation(t *testing.T) {
|
func TestDelete_InputValidation(t *testing.T) {
|
||||||
server := testServer(t)
|
|
||||||
client := testClient(t, server)
|
|
||||||
demo.RegisterTypes(server.Registry)
|
|
||||||
|
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
modFn func(artistId, recordLabelId *pbresource.ID) *pbresource.ID
|
modFn func(artistId, recordLabelId, executiveId *pbresource.ID) *pbresource.ID
|
||||||
errContains string
|
errContains string
|
||||||
}
|
}
|
||||||
|
|
||||||
testCases := map[string]testCase{
|
run := func(t *testing.T, client pbresource.ResourceServiceClient, tc testCase) {
|
||||||
"no id": {
|
executive, err := demo.GenerateV1Executive("marvin", "CEO")
|
||||||
modFn: func(_, _ *pbresource.ID) *pbresource.ID {
|
require.NoError(t, err)
|
||||||
return nil
|
|
||||||
},
|
|
||||||
errContains: "id is required",
|
|
||||||
},
|
|
||||||
"no type": {
|
|
||||||
modFn: func(artistId, _ *pbresource.ID) *pbresource.ID {
|
|
||||||
artistId.Type = nil
|
|
||||||
return artistId
|
|
||||||
},
|
|
||||||
errContains: "id.type is required",
|
|
||||||
},
|
|
||||||
"no name": {
|
|
||||||
modFn: func(artistId, _ *pbresource.ID) *pbresource.ID {
|
|
||||||
artistId.Name = ""
|
|
||||||
return artistId
|
|
||||||
},
|
|
||||||
errContains: "id.name invalid",
|
|
||||||
},
|
|
||||||
"mixed case name": {
|
|
||||||
modFn: func(artistId, _ *pbresource.ID) *pbresource.ID {
|
|
||||||
artistId.Name = "DepecheMode"
|
|
||||||
return artistId
|
|
||||||
},
|
|
||||||
errContains: "id.name invalid",
|
|
||||||
},
|
|
||||||
"name too long": {
|
|
||||||
modFn: func(artistId, _ *pbresource.ID) *pbresource.ID {
|
|
||||||
artistId.Name = strings.Repeat("n", resource.MaxNameLength+1)
|
|
||||||
return artistId
|
|
||||||
},
|
|
||||||
errContains: "id.name invalid",
|
|
||||||
},
|
|
||||||
"partition mixed case": {
|
|
||||||
modFn: func(artistId, _ *pbresource.ID) *pbresource.ID {
|
|
||||||
artistId.Tenancy.Partition = "Default"
|
|
||||||
return artistId
|
|
||||||
},
|
|
||||||
errContains: "id.tenancy.partition invalid",
|
|
||||||
},
|
|
||||||
"partition name too long": {
|
|
||||||
modFn: func(artistId, _ *pbresource.ID) *pbresource.ID {
|
|
||||||
artistId.Tenancy.Partition = strings.Repeat("p", resource.MaxNameLength+1)
|
|
||||||
return artistId
|
|
||||||
},
|
|
||||||
errContains: "id.tenancy.partition invalid",
|
|
||||||
},
|
|
||||||
"namespace mixed case": {
|
|
||||||
modFn: func(artistId, _ *pbresource.ID) *pbresource.ID {
|
|
||||||
artistId.Tenancy.Namespace = "Default"
|
|
||||||
return artistId
|
|
||||||
},
|
|
||||||
errContains: "id.tenancy.namespace invalid",
|
|
||||||
},
|
|
||||||
"namespace name too long": {
|
|
||||||
modFn: func(artistId, _ *pbresource.ID) *pbresource.ID {
|
|
||||||
artistId.Tenancy.Namespace = strings.Repeat("n", resource.MaxNameLength+1)
|
|
||||||
return artistId
|
|
||||||
},
|
|
||||||
errContains: "id.tenancy.namespace invalid",
|
|
||||||
},
|
|
||||||
"partition scoped resource with namespace": {
|
|
||||||
modFn: func(_, recordLabelId *pbresource.ID) *pbresource.ID {
|
|
||||||
recordLabelId.Tenancy.Namespace = "ishouldnothaveanamespace"
|
|
||||||
return recordLabelId
|
|
||||||
},
|
|
||||||
errContains: "cannot have a namespace",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for desc, tc := range testCases {
|
|
||||||
t.Run(desc, func(t *testing.T) {
|
|
||||||
recordLabel, err := demo.GenerateV1RecordLabel("looney-tunes")
|
recordLabel, err := demo.GenerateV1RecordLabel("looney-tunes")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
artist, err := demo.GenerateV2Artist()
|
artist, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
req := &pbresource.DeleteRequest{Id: tc.modFn(artist.Id, recordLabel.Id), Version: ""}
|
req := &pbresource.DeleteRequest{Id: tc.modFn(artist.Id, recordLabel.Id, executive.Id), Version: ""}
|
||||||
|
_, err = client.Delete(context.Background(), req)
|
||||||
_, err = client.Delete(testContext(t), req)
|
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.Equal(t, codes.InvalidArgument.String(), status.Code(err).String())
|
require.Equal(t, codes.InvalidArgument.String(), status.Code(err).String())
|
||||||
require.ErrorContains(t, err, tc.errContains)
|
require.ErrorContains(t, err, tc.errContains)
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := map[string]testCase{
|
||||||
|
"no id": {
|
||||||
|
modFn: func(_, _, _ *pbresource.ID) *pbresource.ID {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
errContains: "id is required",
|
||||||
|
},
|
||||||
|
"no type": {
|
||||||
|
modFn: func(artistId, _, _ *pbresource.ID) *pbresource.ID {
|
||||||
|
artistId.Type = nil
|
||||||
|
return artistId
|
||||||
|
},
|
||||||
|
errContains: "id.type is required",
|
||||||
|
},
|
||||||
|
"no name": {
|
||||||
|
modFn: func(artistId, _, _ *pbresource.ID) *pbresource.ID {
|
||||||
|
artistId.Name = ""
|
||||||
|
return artistId
|
||||||
|
},
|
||||||
|
errContains: "id.name invalid",
|
||||||
|
},
|
||||||
|
"mixed case name": {
|
||||||
|
modFn: func(artistId, _, _ *pbresource.ID) *pbresource.ID {
|
||||||
|
artistId.Name = "DepecheMode"
|
||||||
|
return artistId
|
||||||
|
},
|
||||||
|
errContains: "id.name invalid",
|
||||||
|
},
|
||||||
|
"name too long": {
|
||||||
|
modFn: func(artistId, _, _ *pbresource.ID) *pbresource.ID {
|
||||||
|
artistId.Name = strings.Repeat("n", resource.MaxNameLength+1)
|
||||||
|
return artistId
|
||||||
|
},
|
||||||
|
errContains: "id.name invalid",
|
||||||
|
},
|
||||||
|
"partition mixed case": {
|
||||||
|
modFn: func(artistId, _, _ *pbresource.ID) *pbresource.ID {
|
||||||
|
artistId.Tenancy.Partition = "Default"
|
||||||
|
return artistId
|
||||||
|
},
|
||||||
|
errContains: "id.tenancy.partition invalid",
|
||||||
|
},
|
||||||
|
"partition name too long": {
|
||||||
|
modFn: func(artistId, _, _ *pbresource.ID) *pbresource.ID {
|
||||||
|
artistId.Tenancy.Partition = strings.Repeat("p", resource.MaxNameLength+1)
|
||||||
|
return artistId
|
||||||
|
},
|
||||||
|
errContains: "id.tenancy.partition invalid",
|
||||||
|
},
|
||||||
|
"namespace mixed case": {
|
||||||
|
modFn: func(artistId, _, _ *pbresource.ID) *pbresource.ID {
|
||||||
|
artistId.Tenancy.Namespace = "Default"
|
||||||
|
return artistId
|
||||||
|
},
|
||||||
|
errContains: "id.tenancy.namespace invalid",
|
||||||
|
},
|
||||||
|
"namespace name too long": {
|
||||||
|
modFn: func(artistId, _, _ *pbresource.ID) *pbresource.ID {
|
||||||
|
artistId.Tenancy.Namespace = strings.Repeat("n", resource.MaxNameLength+1)
|
||||||
|
return artistId
|
||||||
|
},
|
||||||
|
errContains: "id.tenancy.namespace invalid",
|
||||||
|
},
|
||||||
|
"partition scoped resource with namespace": {
|
||||||
|
modFn: func(_, recordLabelId, _ *pbresource.ID) *pbresource.ID {
|
||||||
|
recordLabelId.Tenancy.Namespace = "ishouldnothaveanamespace"
|
||||||
|
return recordLabelId
|
||||||
|
},
|
||||||
|
errContains: "cannot have a namespace",
|
||||||
|
},
|
||||||
|
"cluster scoped resource with partition": {
|
||||||
|
modFn: func(_, _, executiveId *pbresource.ID) *pbresource.ID {
|
||||||
|
executiveId.Tenancy.Partition = "ishouldnothaveapartition"
|
||||||
|
executiveId.Tenancy.Namespace = ""
|
||||||
|
return executiveId
|
||||||
|
},
|
||||||
|
errContains: "cannot have a partition",
|
||||||
|
},
|
||||||
|
"cluster scoped resource with namespace": {
|
||||||
|
modFn: func(_, _, executiveId *pbresource.ID) *pbresource.ID {
|
||||||
|
executiveId.Tenancy.Partition = ""
|
||||||
|
executiveId.Tenancy.Namespace = "ishouldnothaveanamespace"
|
||||||
|
return executiveId
|
||||||
|
},
|
||||||
|
errContains: "cannot have a namespace",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, useV2Tenancy := range []bool{false, true} {
|
||||||
|
t.Run(fmt.Sprintf("v2tenancy %v", useV2Tenancy), func(t *testing.T) {
|
||||||
|
client := svctest.NewResourceServiceBuilder().
|
||||||
|
WithV2Tenancy(useV2Tenancy).
|
||||||
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
|
|
||||||
|
for desc, tc := range testCases {
|
||||||
|
t.Run(desc, func(t *testing.T) {
|
||||||
|
run(t, client, tc)
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDelete_TypeNotRegistered(t *testing.T) {
|
func TestDelete_TypeNotRegistered(t *testing.T) {
|
||||||
t.Parallel()
|
for _, useV2Tenancy := range []bool{false, true} {
|
||||||
|
t.Run(fmt.Sprintf("v2tenancy %v", useV2Tenancy), func(t *testing.T) {
|
||||||
|
client := svctest.NewResourceServiceBuilder().WithV2Tenancy(useV2Tenancy).Run(t)
|
||||||
|
|
||||||
_, client, ctx := testDeps(t)
|
|
||||||
artist, err := demo.GenerateV2Artist()
|
artist, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// delete artist with unregistered type
|
// delete artist with unregistered type
|
||||||
_, err = client.Delete(ctx, &pbresource.DeleteRequest{Id: artist.Id, Version: ""})
|
_, err = client.Delete(context.Background(), &pbresource.DeleteRequest{Id: artist.Id, Version: ""})
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.Equal(t, codes.InvalidArgument.String(), status.Code(err).String())
|
require.Equal(t, codes.InvalidArgument.String(), status.Code(err).String())
|
||||||
|
require.ErrorContains(t, err, "not registered")
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDelete_ACLs(t *testing.T) {
|
func TestDelete_ACLs(t *testing.T) {
|
||||||
|
@ -157,9 +193,8 @@ func TestDelete_ACLs(t *testing.T) {
|
||||||
|
|
||||||
for desc, tc := range testcases {
|
for desc, tc := range testcases {
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
server := testServer(t)
|
builder := svctest.NewResourceServiceBuilder().WithRegisterFns(demo.RegisterTypes)
|
||||||
client := testClient(t, server)
|
client := builder.Run(t)
|
||||||
demo.RegisterTypes(server.Registry)
|
|
||||||
|
|
||||||
artist, err := demo.GenerateV2Artist()
|
artist, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -169,10 +204,10 @@ func TestDelete_ACLs(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Mock is put in place after the above "write" since the "write" must also pass the ACL check.
|
// Mock is put in place after the above "write" since the "write" must also pass the ACL check.
|
||||||
mockACLResolver := &MockACLResolver{}
|
mockACLResolver := &svc.MockACLResolver{}
|
||||||
mockACLResolver.On("ResolveTokenAndDefaultMeta", mock.Anything, mock.Anything, mock.Anything).
|
mockACLResolver.On("ResolveTokenAndDefaultMeta", mock.Anything, mock.Anything, mock.Anything).
|
||||||
Return(tc.authz, nil)
|
Return(tc.authz, nil)
|
||||||
server.ACLResolver = mockACLResolver
|
builder.ServiceImpl().Config.ACLResolver = mockACLResolver
|
||||||
|
|
||||||
// Exercise ACL.
|
// Exercise ACL.
|
||||||
_, err = client.Delete(testContext(t), &pbresource.DeleteRequest{Id: rsp.Resource.Id})
|
_, err = client.Delete(testContext(t), &pbresource.DeleteRequest{Id: rsp.Resource.Id})
|
||||||
|
@ -184,12 +219,8 @@ func TestDelete_ACLs(t *testing.T) {
|
||||||
func TestDelete_Success(t *testing.T) {
|
func TestDelete_Success(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
for desc, tc := range deleteTestCases() {
|
run := func(t *testing.T, client pbresource.ResourceServiceClient, tc deleteTestCase, modFn func(artistId, recordlabelId *pbresource.ID) *pbresource.ID) {
|
||||||
t.Run(desc, func(t *testing.T) {
|
ctx := context.Background()
|
||||||
for tenancyDesc, modFn := range tenancyCases() {
|
|
||||||
t.Run(tenancyDesc, func(t *testing.T) {
|
|
||||||
server, client, ctx := testDeps(t)
|
|
||||||
demo.RegisterTypes(server.Registry)
|
|
||||||
|
|
||||||
recordLabel, err := demo.GenerateV1RecordLabel("looney-tunes")
|
recordLabel, err := demo.GenerateV1RecordLabel("looney-tunes")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -223,9 +254,9 @@ func TestDelete_Success(t *testing.T) {
|
||||||
require.Equal(t, codes.NotFound.String(), status.Code(err).String())
|
require.Equal(t, codes.NotFound.String(), status.Code(err).String())
|
||||||
|
|
||||||
// Derive tombstone name from resource that was deleted.
|
// Derive tombstone name from resource that was deleted.
|
||||||
tname := TombstoneNameFor(originalRecordLabelId)
|
tname := svc.TombstoneNameFor(originalRecordLabelId)
|
||||||
if proto.Equal(deleteId.Type, demo.TypeV2Artist) {
|
if proto.Equal(deleteId.Type, demo.TypeV2Artist) {
|
||||||
tname = TombstoneNameFor(originalArtistId)
|
tname = svc.TombstoneNameFor(originalArtistId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify tombstone created
|
// Verify tombstone created
|
||||||
|
@ -237,6 +268,21 @@ func TestDelete_Success(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
require.NoError(t, err, "expected tombstone to be found")
|
require.NoError(t, err, "expected tombstone to be found")
|
||||||
|
}
|
||||||
|
|
||||||
|
for desc, tc := range deleteTestCases() {
|
||||||
|
t.Run(desc, func(t *testing.T) {
|
||||||
|
for tenancyDesc, modFn := range tenancyCases() {
|
||||||
|
t.Run(tenancyDesc, func(t *testing.T) {
|
||||||
|
for _, useV2Tenancy := range []bool{false, true} {
|
||||||
|
t.Run(fmt.Sprintf("v2tenancy %v", useV2Tenancy), func(t *testing.T) {
|
||||||
|
client := svctest.NewResourceServiceBuilder().
|
||||||
|
WithV2Tenancy(useV2Tenancy).
|
||||||
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
|
run(t, client, tc, modFn)
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -246,8 +292,13 @@ func TestDelete_Success(t *testing.T) {
|
||||||
func TestDelete_TombstoneDeletionDoesNotCreateNewTombstone(t *testing.T) {
|
func TestDelete_TombstoneDeletionDoesNotCreateNewTombstone(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
server, client, ctx := testDeps(t)
|
for _, useV2Tenancy := range []bool{false, true} {
|
||||||
demo.RegisterTypes(server.Registry)
|
t.Run(fmt.Sprintf("v2tenancy %v", useV2Tenancy), func(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
client := svctest.NewResourceServiceBuilder().
|
||||||
|
WithV2Tenancy(useV2Tenancy).
|
||||||
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
|
|
||||||
artist, err := demo.GenerateV2Artist()
|
artist, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -263,7 +314,7 @@ func TestDelete_TombstoneDeletionDoesNotCreateNewTombstone(t *testing.T) {
|
||||||
// verify artist's tombstone created
|
// verify artist's tombstone created
|
||||||
rsp2, err := client.Read(ctx, &pbresource.ReadRequest{
|
rsp2, err := client.Read(ctx, &pbresource.ReadRequest{
|
||||||
Id: &pbresource.ID{
|
Id: &pbresource.ID{
|
||||||
Name: TombstoneNameFor(artist.Id),
|
Name: svc.TombstoneNameFor(artist.Id),
|
||||||
Type: resource.TypeV1Tombstone,
|
Type: resource.TypeV1Tombstone,
|
||||||
Tenancy: artist.Id.Tenancy,
|
Tenancy: artist.Id.Tenancy,
|
||||||
},
|
},
|
||||||
|
@ -279,21 +330,34 @@ func TestDelete_TombstoneDeletionDoesNotCreateNewTombstone(t *testing.T) {
|
||||||
rsp3, err := client.List(ctx, &pbresource.ListRequest{Type: resource.TypeV1Tombstone, Tenancy: artist.Id.Tenancy})
|
rsp3, err := client.List(ctx, &pbresource.ListRequest{Type: resource.TypeV1Tombstone, Tenancy: artist.Id.Tenancy})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Empty(t, rsp3.Resources)
|
require.Empty(t, rsp3.Resources)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDelete_NotFound(t *testing.T) {
|
func TestDelete_NotFound(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
for desc, tc := range deleteTestCases() {
|
run := func(t *testing.T, client pbresource.ResourceServiceClient, tc deleteTestCase) {
|
||||||
t.Run(desc, func(t *testing.T) {
|
|
||||||
server, client, ctx := testDeps(t)
|
|
||||||
demo.RegisterTypes(server.Registry)
|
|
||||||
artist, err := demo.GenerateV2Artist()
|
artist, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// verify delete of non-existant or already deleted resource is a no-op
|
// verify delete of non-existant or already deleted resource is a no-op
|
||||||
_, err = client.Delete(ctx, tc.deleteReqFn(artist))
|
_, err = client.Delete(context.Background(), tc.deleteReqFn(artist))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, useV2Tenancy := range []bool{false, true} {
|
||||||
|
t.Run(fmt.Sprintf("v2tenancy %v", useV2Tenancy), func(t *testing.T) {
|
||||||
|
client := svctest.NewResourceServiceBuilder().
|
||||||
|
WithV2Tenancy(useV2Tenancy).
|
||||||
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
|
|
||||||
|
for desc, tc := range deleteTestCases() {
|
||||||
|
t.Run(desc, func(t *testing.T) {
|
||||||
|
run(t, client, tc)
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -301,23 +365,35 @@ func TestDelete_NotFound(t *testing.T) {
|
||||||
func TestDelete_VersionMismatch(t *testing.T) {
|
func TestDelete_VersionMismatch(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
server, client, ctx := testDeps(t)
|
for _, useV2Tenancy := range []bool{false, true} {
|
||||||
demo.RegisterTypes(server.Registry)
|
t.Run(fmt.Sprintf("v2tenancy %v", useV2Tenancy), func(t *testing.T) {
|
||||||
|
client := svctest.NewResourceServiceBuilder().
|
||||||
|
WithV2Tenancy(useV2Tenancy).
|
||||||
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
|
|
||||||
artist, err := demo.GenerateV2Artist()
|
artist, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
rsp, err := client.Write(ctx, &pbresource.WriteRequest{Resource: artist})
|
rsp, err := client.Write(context.Background(), &pbresource.WriteRequest{Resource: artist})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// delete with a version that is different from the stored version
|
// delete with a version that is different from the stored version
|
||||||
_, err = client.Delete(ctx, &pbresource.DeleteRequest{Id: rsp.Resource.Id, Version: "non-existent-version"})
|
_, err = client.Delete(context.Background(), &pbresource.DeleteRequest{Id: rsp.Resource.Id, Version: "non-existent-version"})
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.Equal(t, codes.Aborted.String(), status.Code(err).String())
|
require.Equal(t, codes.Aborted.String(), status.Code(err).String())
|
||||||
require.ErrorContains(t, err, "CAS operation failed")
|
require.ErrorContains(t, err, "CAS operation failed")
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDelete_MarkedForDeletionWhenFinalizersPresent(t *testing.T) {
|
func TestDelete_MarkedForDeletionWhenFinalizersPresent(t *testing.T) {
|
||||||
server, client, ctx := testDeps(t)
|
for _, useV2Tenancy := range []bool{false, true} {
|
||||||
demo.RegisterTypes(server.Registry)
|
t.Run(fmt.Sprintf("v2tenancy %v", useV2Tenancy), func(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
client := svctest.NewResourceServiceBuilder().
|
||||||
|
WithV2Tenancy(useV2Tenancy).
|
||||||
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
|
|
||||||
// Create a resource with a finalizer
|
// Create a resource with a finalizer
|
||||||
res := rtest.Resource(demo.TypeV1Artist, "manwithnoname").
|
res := rtest.Resource(demo.TypeV1Artist, "manwithnoname").
|
||||||
|
@ -343,11 +419,18 @@ func TestDelete_MarkedForDeletionWhenFinalizersPresent(t *testing.T) {
|
||||||
rsp2, err := client.Read(ctx, &pbresource.ReadRequest{Id: res.Id})
|
rsp2, err := client.Read(ctx, &pbresource.ReadRequest{Id: res.Id})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
rtest.RequireVersionUnchanged(t, rsp2.Resource, rsp.Resource.Version)
|
rtest.RequireVersionUnchanged(t, rsp2.Resource, rsp.Resource.Version)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDelete_ImmediatelyDeletedAfterFinalizersRemoved(t *testing.T) {
|
func TestDelete_ImmediatelyDeletedAfterFinalizersRemoved(t *testing.T) {
|
||||||
server, client, ctx := testDeps(t)
|
for _, useV2Tenancy := range []bool{false, true} {
|
||||||
demo.RegisterTypes(server.Registry)
|
t.Run(fmt.Sprintf("v2tenancy %v", useV2Tenancy), func(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
client := svctest.NewResourceServiceBuilder().
|
||||||
|
WithV2Tenancy(useV2Tenancy).
|
||||||
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
|
|
||||||
// Create a resource with a finalizer
|
// Create a resource with a finalizer
|
||||||
res := rtest.Resource(demo.TypeV1Artist, "manwithnoname").
|
res := rtest.Resource(demo.TypeV1Artist, "manwithnoname").
|
||||||
|
@ -375,12 +458,22 @@ func TestDelete_ImmediatelyDeletedAfterFinalizersRemoved(t *testing.T) {
|
||||||
_, err = client.Read(ctx, &pbresource.ReadRequest{Id: rsp.Resource.Id})
|
_, err = client.Read(ctx, &pbresource.ReadRequest{Id: rsp.Resource.Id})
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.Equal(t, codes.NotFound.String(), status.Code(err).String())
|
require.Equal(t, codes.NotFound.String(), status.Code(err).String())
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDeps(t *testing.T) (*Server, pbresource.ResourceServiceClient, context.Context) {
|
func TestDelete_BlockDeleteDefaultNamespace(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().WithV2Tenancy(true).Run(t)
|
||||||
client := testClient(t, server)
|
|
||||||
return server, client, context.Background()
|
id := &pbresource.ID{
|
||||||
|
Name: resource.DefaultNamespaceName,
|
||||||
|
Type: pbtenancy.NamespaceType,
|
||||||
|
Tenancy: &pbresource.Tenancy{Partition: resource.DefaultPartitionName},
|
||||||
|
}
|
||||||
|
_, err := client.Delete(context.Background(), &pbresource.DeleteRequest{Id: id})
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Equal(t, codes.InvalidArgument.String(), status.Code(err).String())
|
||||||
|
require.ErrorContains(t, err, "cannot delete default namespace")
|
||||||
}
|
}
|
||||||
|
|
||||||
type deleteTestCase struct {
|
type deleteTestCase struct {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// // Copyright (c) HashiCorp, Inc.
|
// // Copyright (c) HashiCorp, Inc.
|
||||||
// // SPDX-License-Identifier: BUSL-1.1
|
// // SPDX-License-Identifier: BUSL-1.1
|
||||||
|
|
||||||
package resource
|
package resource_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -17,6 +17,8 @@ import (
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/acl"
|
"github.com/hashicorp/consul/acl"
|
||||||
|
svc "github.com/hashicorp/consul/agent/grpc-external/services/resource"
|
||||||
|
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
|
||||||
"github.com/hashicorp/consul/internal/resource"
|
"github.com/hashicorp/consul/internal/resource"
|
||||||
"github.com/hashicorp/consul/internal/resource/demo"
|
"github.com/hashicorp/consul/internal/resource/demo"
|
||||||
"github.com/hashicorp/consul/internal/resource/resourcetest"
|
"github.com/hashicorp/consul/internal/resource/resourcetest"
|
||||||
|
@ -25,10 +27,12 @@ import (
|
||||||
"github.com/hashicorp/consul/proto/private/prototest"
|
"github.com/hashicorp/consul/proto/private/prototest"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO: Update all tests to use true/false table test for v2tenancy
|
||||||
|
|
||||||
func TestListByOwner_InputValidation(t *testing.T) {
|
func TestListByOwner_InputValidation(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
demo.RegisterTypes(server.Registry)
|
Run(t)
|
||||||
|
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
modFn func(artistId, recordlabelId, executiveId *pbresource.ID) *pbresource.ID
|
modFn func(artistId, recordlabelId, executiveId *pbresource.ID) *pbresource.ID
|
||||||
|
@ -152,8 +156,7 @@ func TestListByOwner_InputValidation(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestListByOwner_TypeNotRegistered(t *testing.T) {
|
func TestListByOwner_TypeNotRegistered(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().Run(t)
|
||||||
client := testClient(t, server)
|
|
||||||
|
|
||||||
_, err := client.ListByOwner(context.Background(), &pbresource.ListByOwnerRequest{
|
_, err := client.ListByOwner(context.Background(), &pbresource.ListByOwnerRequest{
|
||||||
Owner: &pbresource.ID{
|
Owner: &pbresource.ID{
|
||||||
|
@ -169,9 +172,9 @@ func TestListByOwner_TypeNotRegistered(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestListByOwner_Empty(t *testing.T) {
|
func TestListByOwner_Empty(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
demo.RegisterTypes(server.Registry)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
client := testClient(t, server)
|
Run(t)
|
||||||
|
|
||||||
res, err := demo.GenerateV2Artist()
|
res, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -185,9 +188,9 @@ func TestListByOwner_Empty(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestListByOwner_Many(t *testing.T) {
|
func TestListByOwner_Many(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
demo.RegisterTypes(server.Registry)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
client := testClient(t, server)
|
Run(t)
|
||||||
|
|
||||||
res, err := demo.GenerateV2Artist()
|
res, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -245,9 +248,9 @@ func TestListByOwner_OwnerTenancyDoesNotExist(t *testing.T) {
|
||||||
}
|
}
|
||||||
for desc, tc := range tenancyCases {
|
for desc, tc := range tenancyCases {
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
demo.RegisterTypes(server.Registry)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
client := testClient(t, server)
|
Run(t)
|
||||||
|
|
||||||
recordLabel := resourcetest.Resource(demo.TypeV1RecordLabel, "looney-tunes").
|
recordLabel := resourcetest.Resource(demo.TypeV1RecordLabel, "looney-tunes").
|
||||||
WithTenancy(resource.DefaultPartitionedTenancy()).
|
WithTenancy(resource.DefaultPartitionedTenancy()).
|
||||||
|
@ -271,9 +274,9 @@ func TestListByOwner_OwnerTenancyDoesNotExist(t *testing.T) {
|
||||||
func TestListByOwner_Tenancy_Defaults_And_Normalization(t *testing.T) {
|
func TestListByOwner_Tenancy_Defaults_And_Normalization(t *testing.T) {
|
||||||
for tenancyDesc, modFn := range tenancyCases() {
|
for tenancyDesc, modFn := range tenancyCases() {
|
||||||
t.Run(tenancyDesc, func(t *testing.T) {
|
t.Run(tenancyDesc, func(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
demo.RegisterTypes(server.Registry)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
client := testClient(t, server)
|
Run(t)
|
||||||
|
|
||||||
// Create partition scoped recordLabel.
|
// Create partition scoped recordLabel.
|
||||||
recordLabel, err := demo.GenerateV1RecordLabel("looney-tunes")
|
recordLabel, err := demo.GenerateV1RecordLabel("looney-tunes")
|
||||||
|
@ -343,9 +346,9 @@ func TestListByOwner_ACL_PerTypeAllowed(t *testing.T) {
|
||||||
|
|
||||||
// roundtrip a ListByOwner which attempts to return a single resource
|
// roundtrip a ListByOwner which attempts to return a single resource
|
||||||
func roundTripListByOwner(t *testing.T, authz acl.Authorizer) (*pbresource.Resource, *pbresource.ListByOwnerResponse, error) {
|
func roundTripListByOwner(t *testing.T, authz acl.Authorizer) (*pbresource.Resource, *pbresource.ListByOwnerResponse, error) {
|
||||||
server := testServer(t)
|
builder := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithRegisterFns(demo.RegisterTypes)
|
||||||
demo.RegisterTypes(server.Registry)
|
client := builder.Run(t)
|
||||||
|
|
||||||
artist, err := demo.GenerateV2Artist()
|
artist, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -361,10 +364,11 @@ func roundTripListByOwner(t *testing.T, authz acl.Authorizer) (*pbresource.Resou
|
||||||
album = rsp2.Resource
|
album = rsp2.Resource
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
mockACLResolver := &MockACLResolver{}
|
// Mock has to be put in place after the above writes so writes will succeed.
|
||||||
|
mockACLResolver := &svc.MockACLResolver{}
|
||||||
mockACLResolver.On("ResolveTokenAndDefaultMeta", mock.Anything, mock.Anything, mock.Anything).
|
mockACLResolver.On("ResolveTokenAndDefaultMeta", mock.Anything, mock.Anything, mock.Anything).
|
||||||
Return(authz, nil)
|
Return(authz, nil)
|
||||||
server.ACLResolver = mockACLResolver
|
builder.ServiceImpl().ACLResolver = mockACLResolver
|
||||||
|
|
||||||
rsp3, err := client.ListByOwner(testContext(t), &pbresource.ListByOwnerRequest{Owner: artist.Id})
|
rsp3, err := client.ListByOwner(testContext(t), &pbresource.ListByOwnerRequest{Owner: artist.Id})
|
||||||
return album, rsp3, err
|
return album, rsp3, err
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright (c) HashiCorp, Inc.
|
// Copyright (c) HashiCorp, Inc.
|
||||||
// SPDX-License-Identifier: BUSL-1.1
|
// SPDX-License-Identifier: BUSL-1.1
|
||||||
|
|
||||||
package resource
|
package resource_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -10,25 +10,29 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/acl"
|
"github.com/hashicorp/consul/acl"
|
||||||
|
svc "github.com/hashicorp/consul/agent/grpc-external/services/resource"
|
||||||
|
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
|
||||||
"github.com/hashicorp/consul/agent/grpc-external/testutils"
|
"github.com/hashicorp/consul/agent/grpc-external/testutils"
|
||||||
"github.com/hashicorp/consul/internal/resource"
|
"github.com/hashicorp/consul/internal/resource"
|
||||||
"github.com/hashicorp/consul/internal/resource/demo"
|
"github.com/hashicorp/consul/internal/resource/demo"
|
||||||
"github.com/hashicorp/consul/internal/storage"
|
"github.com/hashicorp/consul/internal/storage"
|
||||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||||
"github.com/hashicorp/consul/proto/private/prototest"
|
"github.com/hashicorp/consul/proto/private/prototest"
|
||||||
|
|
||||||
"github.com/stretchr/testify/mock"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
"google.golang.org/grpc/codes"
|
|
||||||
"google.golang.org/grpc/metadata"
|
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO: Update all tests to use true/false table test for v2tenancy
|
||||||
|
|
||||||
func TestList_InputValidation(t *testing.T) {
|
func TestList_InputValidation(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
demo.RegisterTypes(server.Registry)
|
Run(t)
|
||||||
|
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
modReqFn func(req *pbresource.ListRequest)
|
modReqFn func(req *pbresource.ListRequest)
|
||||||
|
@ -93,8 +97,7 @@ func TestList_InputValidation(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestList_TypeNotFound(t *testing.T) {
|
func TestList_TypeNotFound(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().Run(t)
|
||||||
client := testClient(t, server)
|
|
||||||
|
|
||||||
_, err := client.List(context.Background(), &pbresource.ListRequest{
|
_, err := client.List(context.Background(), &pbresource.ListRequest{
|
||||||
Type: demo.TypeV2Artist,
|
Type: demo.TypeV2Artist,
|
||||||
|
@ -109,9 +112,9 @@ func TestList_TypeNotFound(t *testing.T) {
|
||||||
func TestList_Empty(t *testing.T) {
|
func TestList_Empty(t *testing.T) {
|
||||||
for desc, tc := range listTestCases() {
|
for desc, tc := range listTestCases() {
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
demo.RegisterTypes(server.Registry)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
client := testClient(t, server)
|
Run(t)
|
||||||
|
|
||||||
rsp, err := client.List(tc.ctx, &pbresource.ListRequest{
|
rsp, err := client.List(tc.ctx, &pbresource.ListRequest{
|
||||||
Type: demo.TypeV1Artist,
|
Type: demo.TypeV1Artist,
|
||||||
|
@ -127,9 +130,9 @@ func TestList_Empty(t *testing.T) {
|
||||||
func TestList_Many(t *testing.T) {
|
func TestList_Many(t *testing.T) {
|
||||||
for desc, tc := range listTestCases() {
|
for desc, tc := range listTestCases() {
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
demo.RegisterTypes(server.Registry)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
client := testClient(t, server)
|
Run(t)
|
||||||
|
|
||||||
resources := make([]*pbresource.Resource, 10)
|
resources := make([]*pbresource.Resource, 10)
|
||||||
for i := 0; i < len(resources); i++ {
|
for i := 0; i < len(resources); i++ {
|
||||||
|
@ -159,9 +162,9 @@ func TestList_Many(t *testing.T) {
|
||||||
func TestList_NamePrefix(t *testing.T) {
|
func TestList_NamePrefix(t *testing.T) {
|
||||||
for desc, tc := range listTestCases() {
|
for desc, tc := range listTestCases() {
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
demo.RegisterTypes(server.Registry)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
client := testClient(t, server)
|
Run(t)
|
||||||
|
|
||||||
expectedResources := []*pbresource.Resource{}
|
expectedResources := []*pbresource.Resource{}
|
||||||
|
|
||||||
|
@ -201,9 +204,9 @@ func TestList_Tenancy_Defaults_And_Normalization(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
for desc, tc := range wildcardTenancyCases() {
|
for desc, tc := range wildcardTenancyCases() {
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
demo.RegisterTypes(server.Registry)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
client := testClient(t, server)
|
Run(t)
|
||||||
|
|
||||||
// Write partition scoped record label
|
// Write partition scoped record label
|
||||||
recordLabel, err := demo.GenerateV1RecordLabel("looney-tunes")
|
recordLabel, err := demo.GenerateV1RecordLabel("looney-tunes")
|
||||||
|
@ -236,14 +239,14 @@ func TestList_Tenancy_Defaults_And_Normalization(t *testing.T) {
|
||||||
func TestList_GroupVersionMismatch(t *testing.T) {
|
func TestList_GroupVersionMismatch(t *testing.T) {
|
||||||
for desc, tc := range listTestCases() {
|
for desc, tc := range listTestCases() {
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
demo.RegisterTypes(server.Registry)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
client := testClient(t, server)
|
Run(t)
|
||||||
|
|
||||||
artist, err := demo.GenerateV2Artist()
|
artist, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = server.Backend.WriteCAS(tc.ctx, artist)
|
_, err = client.Write(tc.ctx, &pbresource.WriteRequest{Resource: artist})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
rsp, err := client.List(tc.ctx, &pbresource.ListRequest{
|
rsp, err := client.List(tc.ctx, &pbresource.ListRequest{
|
||||||
|
@ -261,7 +264,7 @@ func TestList_VerifyReadConsistencyArg(t *testing.T) {
|
||||||
// Uses a mockBackend instead of the inmem Backend to verify the ReadConsistency argument is set correctly.
|
// Uses a mockBackend instead of the inmem Backend to verify the ReadConsistency argument is set correctly.
|
||||||
for desc, tc := range listTestCases() {
|
for desc, tc := range listTestCases() {
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
mockBackend := NewMockBackend(t)
|
mockBackend := svc.NewMockBackend(t)
|
||||||
server := testServer(t)
|
server := testServer(t)
|
||||||
server.Backend = mockBackend
|
server.Backend = mockBackend
|
||||||
demo.RegisterTypes(server.Registry)
|
demo.RegisterTypes(server.Registry)
|
||||||
|
@ -322,25 +325,24 @@ func TestList_ACL_ListAllowed_ReadAllowed(t *testing.T) {
|
||||||
prototest.AssertDeepEqual(t, artist, rsp.Resources[0])
|
prototest.AssertDeepEqual(t, artist, rsp.Resources[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
// roundtrip a List which attempts to return a single resource
|
|
||||||
func roundTripList(t *testing.T, authz acl.Authorizer) (*pbresource.Resource, *pbresource.ListResponse, error) {
|
func roundTripList(t *testing.T, authz acl.Authorizer) (*pbresource.Resource, *pbresource.ListResponse, error) {
|
||||||
server := testServer(t)
|
|
||||||
client := testClient(t, server)
|
|
||||||
ctx := testContext(t)
|
ctx := testContext(t)
|
||||||
|
builder := svctest.NewResourceServiceBuilder().WithRegisterFns(demo.RegisterTypes)
|
||||||
mockACLResolver := &MockACLResolver{}
|
client := builder.Run(t)
|
||||||
mockACLResolver.On("ResolveTokenAndDefaultMeta", mock.Anything, mock.Anything, mock.Anything).
|
|
||||||
Return(authz, nil)
|
|
||||||
server.ACLResolver = mockACLResolver
|
|
||||||
demo.RegisterTypes(server.Registry)
|
|
||||||
|
|
||||||
artist, err := demo.GenerateV2Artist()
|
artist, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
artist, err = server.Backend.WriteCAS(ctx, artist)
|
rsp1, err := client.Write(ctx, &pbresource.WriteRequest{Resource: artist})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
rsp, err := client.List(
|
// Put ACLResolver in place after above writes so writes not subject to ACLs
|
||||||
|
mockACLResolver := &svc.MockACLResolver{}
|
||||||
|
mockACLResolver.On("ResolveTokenAndDefaultMeta", mock.Anything, mock.Anything, mock.Anything).
|
||||||
|
Return(authz, nil)
|
||||||
|
builder.ServiceImpl().Config.ACLResolver = mockACLResolver
|
||||||
|
|
||||||
|
rsp2, err := client.List(
|
||||||
ctx,
|
ctx,
|
||||||
&pbresource.ListRequest{
|
&pbresource.ListRequest{
|
||||||
Type: artist.Id.Type,
|
Type: artist.Id.Type,
|
||||||
|
@ -348,8 +350,7 @@ func roundTripList(t *testing.T, authz acl.Authorizer) (*pbresource.Resource, *p
|
||||||
NamePrefix: "",
|
NamePrefix: "",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
return rsp1.Resource, rsp2, err
|
||||||
return artist, rsp, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type listTestCase struct {
|
type listTestCase struct {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright (c) HashiCorp, Inc.
|
// Copyright (c) HashiCorp, Inc.
|
||||||
// SPDX-License-Identifier: BUSL-1.1
|
// SPDX-License-Identifier: BUSL-1.1
|
||||||
|
|
||||||
package resource
|
package resource_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -19,21 +19,23 @@ import (
|
||||||
|
|
||||||
"github.com/hashicorp/consul/acl"
|
"github.com/hashicorp/consul/acl"
|
||||||
"github.com/hashicorp/consul/acl/resolver"
|
"github.com/hashicorp/consul/acl/resolver"
|
||||||
|
svc "github.com/hashicorp/consul/agent/grpc-external/services/resource"
|
||||||
|
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
|
||||||
"github.com/hashicorp/consul/agent/grpc-external/testutils"
|
"github.com/hashicorp/consul/agent/grpc-external/testutils"
|
||||||
"github.com/hashicorp/consul/internal/resource"
|
"github.com/hashicorp/consul/internal/resource"
|
||||||
"github.com/hashicorp/consul/internal/resource/demo"
|
"github.com/hashicorp/consul/internal/resource/demo"
|
||||||
"github.com/hashicorp/consul/internal/storage"
|
"github.com/hashicorp/consul/internal/storage"
|
||||||
"github.com/hashicorp/consul/internal/tenancy"
|
|
||||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||||
"github.com/hashicorp/consul/proto/private/prototest"
|
"github.com/hashicorp/consul/proto/private/prototest"
|
||||||
"github.com/hashicorp/consul/sdk/testutil"
|
"github.com/hashicorp/consul/sdk/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO: Update all tests to use true/false table test for v2tenancy
|
||||||
|
|
||||||
func TestRead_InputValidation(t *testing.T) {
|
func TestRead_InputValidation(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
tenancy.RegisterTypes(server.Registry)
|
Run(t)
|
||||||
demo.RegisterTypes(server.Registry)
|
|
||||||
|
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
modFn func(artistId, recordlabelId, executiveId *pbresource.ID) *pbresource.ID
|
modFn func(artistId, recordlabelId, executiveId *pbresource.ID) *pbresource.ID
|
||||||
|
@ -148,7 +150,7 @@ func TestRead_InputValidation(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRead_TypeNotFound(t *testing.T) {
|
func TestRead_TypeNotFound(t *testing.T) {
|
||||||
server := NewServer(Config{Registry: resource.NewRegistry()})
|
server := svc.NewServer(svc.Config{Registry: resource.NewRegistry()})
|
||||||
client := testClient(t, server)
|
client := testClient(t, server)
|
||||||
|
|
||||||
artist, err := demo.GenerateV2Artist()
|
artist, err := demo.GenerateV2Artist()
|
||||||
|
@ -202,18 +204,19 @@ func TestRead_ResourceNotFound(t *testing.T) {
|
||||||
}
|
}
|
||||||
for tenancyDesc, tenancyCase := range tenancyCases {
|
for tenancyDesc, tenancyCase := range tenancyCases {
|
||||||
t.Run(tenancyDesc, func(t *testing.T) {
|
t.Run(tenancyDesc, func(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
demo.RegisterTypes(server.Registry)
|
WithV2Tenancy(true).
|
||||||
client := testClient(t, server)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
|
|
||||||
recordLabel, err := demo.GenerateV1RecordLabel("looney-tunes")
|
recordLabel, err := demo.GenerateV1RecordLabel("looney-tunes")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
recordLabel, err = server.Backend.WriteCAS(tc.ctx, recordLabel)
|
_, err = client.Write(context.Background(), &pbresource.WriteRequest{Resource: recordLabel})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
artist, err := demo.GenerateV2Artist()
|
artist, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
artist, err = server.Backend.WriteCAS(tc.ctx, artist)
|
_, err = client.Write(context.Background(), &pbresource.WriteRequest{Resource: artist})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Each tenancy test case picks which resource to use based on the resource type's scope.
|
// Each tenancy test case picks which resource to use based on the resource type's scope.
|
||||||
|
@ -230,15 +233,14 @@ func TestRead_ResourceNotFound(t *testing.T) {
|
||||||
func TestRead_GroupVersionMismatch(t *testing.T) {
|
func TestRead_GroupVersionMismatch(t *testing.T) {
|
||||||
for desc, tc := range readTestCases() {
|
for desc, tc := range readTestCases() {
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
demo.RegisterTypes(server.Registry)
|
Run(t)
|
||||||
client := testClient(t, server)
|
|
||||||
|
|
||||||
artist, err := demo.GenerateV2Artist()
|
artist, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = server.Backend.WriteCAS(tc.ctx, artist)
|
_, err = client.Write(tc.ctx, &pbresource.WriteRequest{Resource: artist})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
id := clone(artist.Id)
|
id := clone(artist.Id)
|
||||||
|
@ -257,18 +259,20 @@ func TestRead_Success(t *testing.T) {
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
for tenancyDesc, modFn := range tenancyCases() {
|
for tenancyDesc, modFn := range tenancyCases() {
|
||||||
t.Run(tenancyDesc, func(t *testing.T) {
|
t.Run(tenancyDesc, func(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
demo.RegisterTypes(server.Registry)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
client := testClient(t, server)
|
Run(t)
|
||||||
|
|
||||||
recordLabel, err := demo.GenerateV1RecordLabel("looney-tunes")
|
recordLabel, err := demo.GenerateV1RecordLabel("looney-tunes")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
recordLabel, err = server.Backend.WriteCAS(tc.ctx, recordLabel)
|
rsp1, err := client.Write(tc.ctx, &pbresource.WriteRequest{Resource: recordLabel})
|
||||||
|
recordLabel = rsp1.Resource
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
artist, err := demo.GenerateV2Artist()
|
artist, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
artist, err = server.Backend.WriteCAS(tc.ctx, artist)
|
rsp2, err := client.Write(tc.ctx, &pbresource.WriteRequest{Resource: artist})
|
||||||
|
artist = rsp2.Resource
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Each tenancy test case picks which resource to use based on the resource type's scope.
|
// Each tenancy test case picks which resource to use based on the resource type's scope.
|
||||||
|
@ -295,7 +299,7 @@ func TestRead_VerifyReadConsistencyArg(t *testing.T) {
|
||||||
for desc, tc := range readTestCases() {
|
for desc, tc := range readTestCases() {
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
server := testServer(t)
|
server := testServer(t)
|
||||||
mockBackend := NewMockBackend(t)
|
mockBackend := svc.NewMockBackend(t)
|
||||||
server.Backend = mockBackend
|
server.Backend = mockBackend
|
||||||
demo.RegisterTypes(server.Registry)
|
demo.RegisterTypes(server.Registry)
|
||||||
|
|
||||||
|
@ -364,15 +368,14 @@ func TestRead_ACLs(t *testing.T) {
|
||||||
|
|
||||||
for desc, tc := range testcases {
|
for desc, tc := range testcases {
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
server := testServer(t)
|
|
||||||
client := testClient(t, server)
|
|
||||||
|
|
||||||
dr := &dummyACLResolver{
|
dr := &dummyACLResolver{
|
||||||
result: testutils.ACLsDisabled(t),
|
result: testutils.ACLsDisabled(t),
|
||||||
}
|
}
|
||||||
server.ACLResolver = dr
|
|
||||||
|
|
||||||
demo.RegisterTypes(server.Registry)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
WithACLResolver(dr).
|
||||||
|
Run(t)
|
||||||
|
|
||||||
dr.SetResult(tc.authz)
|
dr.SetResult(tc.authz)
|
||||||
testutil.RunStep(t, "does not exist", func(t *testing.T) {
|
testutil.RunStep(t, "does not exist", func(t *testing.T) {
|
||||||
|
@ -410,7 +413,7 @@ type dummyACLResolver struct {
|
||||||
result resolver.Result
|
result resolver.Result
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ ACLResolver = (*dummyACLResolver)(nil)
|
var _ svc.ACLResolver = (*dummyACLResolver)(nil)
|
||||||
|
|
||||||
func (r *dummyACLResolver) SetResult(result resolver.Result) {
|
func (r *dummyACLResolver) SetResult(result resolver.Result) {
|
||||||
r.lock.Lock()
|
r.lock.Lock()
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
//go:build !consulent
|
//go:build !consulent
|
||||||
|
|
||||||
package resource
|
package resource_test
|
||||||
|
|
||||||
import "github.com/hashicorp/consul/acl"
|
import "github.com/hashicorp/consul/acl"
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright (c) HashiCorp, Inc.
|
// Copyright (c) HashiCorp, Inc.
|
||||||
// SPDX-License-Identifier: BUSL-1.1
|
// SPDX-License-Identifier: BUSL-1.1
|
||||||
|
|
||||||
package resource
|
package resource_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -11,12 +11,14 @@ import (
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
"google.golang.org/protobuf/types/known/anypb"
|
"google.golang.org/protobuf/types/known/anypb"
|
||||||
|
|
||||||
"github.com/hashicorp/go-uuid"
|
"github.com/hashicorp/go-uuid"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/acl"
|
"github.com/hashicorp/consul/acl"
|
||||||
"github.com/hashicorp/consul/acl/resolver"
|
"github.com/hashicorp/consul/acl/resolver"
|
||||||
|
svc "github.com/hashicorp/consul/agent/grpc-external/services/resource"
|
||||||
"github.com/hashicorp/consul/agent/grpc-external/testutils"
|
"github.com/hashicorp/consul/agent/grpc-external/testutils"
|
||||||
"github.com/hashicorp/consul/agent/structs"
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
"github.com/hashicorp/consul/internal/resource"
|
"github.com/hashicorp/consul/internal/resource"
|
||||||
|
@ -51,7 +53,8 @@ func AuthorizerFrom(t *testing.T, policyStrs ...string) resolver.Result {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testServer(t *testing.T) *Server {
|
// Deprecated: use NewResourceServiceBuilder instead
|
||||||
|
func testServer(t *testing.T) *svc.Server {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
backend, err := inmem.NewBackend()
|
backend, err := inmem.NewBackend()
|
||||||
|
@ -59,7 +62,7 @@ func testServer(t *testing.T) *Server {
|
||||||
go backend.Run(testContext(t))
|
go backend.Run(testContext(t))
|
||||||
|
|
||||||
// Mock the ACL Resolver to "allow all" for testing.
|
// Mock the ACL Resolver to "allow all" for testing.
|
||||||
mockACLResolver := &MockACLResolver{}
|
mockACLResolver := &svc.MockACLResolver{}
|
||||||
mockACLResolver.On("ResolveTokenAndDefaultMeta", mock.Anything, mock.Anything, mock.Anything).
|
mockACLResolver.On("ResolveTokenAndDefaultMeta", mock.Anything, mock.Anything, mock.Anything).
|
||||||
Return(testutils.ACLsDisabled(t), nil).
|
Return(testutils.ACLsDisabled(t), nil).
|
||||||
Run(func(args mock.Arguments) {
|
Run(func(args mock.Arguments) {
|
||||||
|
@ -76,7 +79,7 @@ func testServer(t *testing.T) *Server {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Mock the 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 := &svc.MockTenancyBridge{}
|
||||||
mockTenancyBridge.On("PartitionExists", resource.DefaultPartitionName).Return(true, nil)
|
mockTenancyBridge.On("PartitionExists", resource.DefaultPartitionName).Return(true, nil)
|
||||||
mockTenancyBridge.On("NamespaceExists", resource.DefaultPartitionName, resource.DefaultNamespaceName).Return(true, nil)
|
mockTenancyBridge.On("NamespaceExists", resource.DefaultPartitionName, resource.DefaultNamespaceName).Return(true, nil)
|
||||||
mockTenancyBridge.On("PartitionExists", mock.Anything).Return(false, nil)
|
mockTenancyBridge.On("PartitionExists", mock.Anything).Return(false, nil)
|
||||||
|
@ -84,7 +87,7 @@ func testServer(t *testing.T) *Server {
|
||||||
mockTenancyBridge.On("IsPartitionMarkedForDeletion", resource.DefaultPartitionName).Return(false, nil)
|
mockTenancyBridge.On("IsPartitionMarkedForDeletion", resource.DefaultPartitionName).Return(false, nil)
|
||||||
mockTenancyBridge.On("IsNamespaceMarkedForDeletion", resource.DefaultPartitionName, resource.DefaultNamespaceName).Return(false, nil)
|
mockTenancyBridge.On("IsNamespaceMarkedForDeletion", resource.DefaultPartitionName, resource.DefaultNamespaceName).Return(false, nil)
|
||||||
|
|
||||||
return NewServer(Config{
|
return svc.NewServer(svc.Config{
|
||||||
Logger: testutil.Logger(t),
|
Logger: testutil.Logger(t),
|
||||||
Registry: resource.NewRegistry(),
|
Registry: resource.NewRegistry(),
|
||||||
Backend: backend,
|
Backend: backend,
|
||||||
|
@ -93,7 +96,8 @@ func testServer(t *testing.T) *Server {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func testClient(t *testing.T, server *Server) pbresource.ResourceServiceClient {
|
// Deprecated: use NewResourceServiceBuilder instead
|
||||||
|
func testClient(t *testing.T, server *svc.Server) pbresource.ResourceServiceClient {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
addr := testutils.RunTestServer(t, server)
|
addr := testutils.RunTestServer(t, server)
|
||||||
|
@ -253,3 +257,5 @@ func tenancyCases() map[string]func(artistId, recordlabelId *pbresource.ID) *pbr
|
||||||
}
|
}
|
||||||
return tenancyCases
|
return tenancyCases
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func clone[T proto.Message](v T) T { return proto.Clone(v).(T) }
|
||||||
|
|
|
@ -0,0 +1,201 @@
|
||||||
|
// Copyright (c) HashiCorp, Inc.
|
||||||
|
// SPDX-License-Identifier: BUSL-1.1
|
||||||
|
|
||||||
|
package testing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/acl"
|
||||||
|
svc "github.com/hashicorp/consul/agent/grpc-external/services/resource"
|
||||||
|
"github.com/hashicorp/consul/agent/grpc-external/testutils"
|
||||||
|
internal "github.com/hashicorp/consul/agent/grpc-internal"
|
||||||
|
"github.com/hashicorp/consul/internal/resource"
|
||||||
|
"github.com/hashicorp/consul/internal/storage/inmem"
|
||||||
|
"github.com/hashicorp/consul/internal/tenancy"
|
||||||
|
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||||
|
"github.com/hashicorp/consul/sdk/testutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
type builder struct {
|
||||||
|
registry resource.Registry
|
||||||
|
registerFns []func(resource.Registry)
|
||||||
|
useV2Tenancy bool
|
||||||
|
tenancies []*pbresource.Tenancy
|
||||||
|
aclResolver svc.ACLResolver
|
||||||
|
serviceImpl *svc.Server
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewResourceServiceBuilder is the preferred way to configure and run
|
||||||
|
// an isolated in-process instance of the resource service for unit
|
||||||
|
// testing. The final call to `Run()` returns a client you can use for
|
||||||
|
// making requests.
|
||||||
|
func NewResourceServiceBuilder() *builder {
|
||||||
|
b := &builder{
|
||||||
|
useV2Tenancy: false,
|
||||||
|
registry: resource.NewRegistry(),
|
||||||
|
// Regardless of whether using mock of v2tenancy, always make sure
|
||||||
|
// the builtin tenancy exists.
|
||||||
|
tenancies: []*pbresource.Tenancy{resource.DefaultNamespacedTenancy()},
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithV2Tenancy configures which tenancy bridge is used.
|
||||||
|
//
|
||||||
|
// true => real v2 default partition and namespace via v2 tenancy bridge
|
||||||
|
// false => mock default partition and namespace since v1 tenancy bridge can't be used (not spinning up an entire server here)
|
||||||
|
func (b *builder) WithV2Tenancy(useV2Tenancy bool) *builder {
|
||||||
|
b.useV2Tenancy = useV2Tenancy
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registry provides access to the constructed registry post-Run() when
|
||||||
|
// needed by other test dependencies.
|
||||||
|
func (b *builder) Registry() resource.Registry {
|
||||||
|
return b.registry
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceImpl provides access to the actual server side implemenation of the resource service. This should never be used
|
||||||
|
// used/accessed without good reason. The current justifying use case is to monkeypatch the ACL resolver post-creation
|
||||||
|
// to allow unfettered writes which some ACL related tests require to put test data in place.
|
||||||
|
func (b *builder) ServiceImpl() *svc.Server {
|
||||||
|
return b.serviceImpl
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) WithRegisterFns(registerFns ...func(resource.Registry)) *builder {
|
||||||
|
for _, registerFn := range registerFns {
|
||||||
|
b.registerFns = append(b.registerFns, registerFn)
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) WithACLResolver(aclResolver svc.ACLResolver) *builder {
|
||||||
|
b.aclResolver = aclResolver
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithTenancies adds additional partitions and namespaces if default/default
|
||||||
|
// is not sufficient.
|
||||||
|
func (b *builder) WithTenancies(tenancies ...*pbresource.Tenancy) *builder {
|
||||||
|
for _, tenancy := range tenancies {
|
||||||
|
b.tenancies = append(b.tenancies, tenancy)
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run starts the resource service and returns a client.
|
||||||
|
func (b *builder) Run(t *testing.T) pbresource.ResourceServiceClient {
|
||||||
|
// backend cannot be customized
|
||||||
|
backend, err := inmem.NewBackend()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// start the backend and add teardown hook
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
t.Cleanup(cancel)
|
||||||
|
go backend.Run(ctx)
|
||||||
|
|
||||||
|
// Automatically add tenancy types if v2 tenancy enabled
|
||||||
|
if b.useV2Tenancy {
|
||||||
|
b.registerFns = append(b.registerFns, tenancy.RegisterTypes)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, registerFn := range b.registerFns {
|
||||||
|
registerFn(b.registry)
|
||||||
|
}
|
||||||
|
|
||||||
|
var tenancyBridge resource.TenancyBridge
|
||||||
|
if !b.useV2Tenancy {
|
||||||
|
// use mock tenancy bridge. default/default has already been added out of the box
|
||||||
|
mockTenancyBridge := &svc.MockTenancyBridge{}
|
||||||
|
|
||||||
|
for _, tenancy := range b.tenancies {
|
||||||
|
mockTenancyBridge.On("PartitionExists", tenancy.Partition).Return(true, nil)
|
||||||
|
mockTenancyBridge.On("NamespaceExists", tenancy.Partition, tenancy.Namespace).Return(true, nil)
|
||||||
|
mockTenancyBridge.On("IsPartitionMarkedForDeletion", tenancy.Partition).Return(false, nil)
|
||||||
|
mockTenancyBridge.On("IsNamespaceMarkedForDeletion", tenancy.Partition, tenancy.Namespace).Return(false, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
tenancyBridge = mockTenancyBridge
|
||||||
|
} else {
|
||||||
|
// use v2 tenancy bridge. population comes later after client injected.
|
||||||
|
tenancyBridge = tenancy.NewV2TenancyBridge()
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.aclResolver == nil {
|
||||||
|
// When not provided (regardless of V1 tenancy or V2 tenancy), configure an ACL resolver
|
||||||
|
// that has ACLs disabled and fills in "default" for the partition and namespace when
|
||||||
|
// not provided. This is similar to user initiated requests.
|
||||||
|
//
|
||||||
|
// Controllers under test should be providing full tenancy since they will run with the DANGER_NO_AUTH.
|
||||||
|
mockACLResolver := &svc.MockACLResolver{}
|
||||||
|
mockACLResolver.On("ResolveTokenAndDefaultMeta", mock.Anything, mock.Anything, mock.Anything).
|
||||||
|
Return(testutils.ACLsDisabled(t), nil).
|
||||||
|
Run(func(args mock.Arguments) {
|
||||||
|
// Caller expecting passed in tokenEntMeta and authorizerContext to be filled in.
|
||||||
|
tokenEntMeta := args.Get(1).(*acl.EnterpriseMeta)
|
||||||
|
if tokenEntMeta != nil {
|
||||||
|
FillEntMeta(tokenEntMeta)
|
||||||
|
}
|
||||||
|
|
||||||
|
authzContext := args.Get(2).(*acl.AuthorizerContext)
|
||||||
|
if authzContext != nil {
|
||||||
|
FillAuthorizerContext(authzContext)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
b.aclResolver = mockACLResolver
|
||||||
|
}
|
||||||
|
|
||||||
|
config := svc.Config{
|
||||||
|
Logger: testutil.Logger(t),
|
||||||
|
Registry: b.registry,
|
||||||
|
Backend: backend,
|
||||||
|
ACLResolver: b.aclResolver,
|
||||||
|
TenancyBridge: tenancyBridge,
|
||||||
|
UseV2Tenancy: b.useV2Tenancy,
|
||||||
|
}
|
||||||
|
|
||||||
|
server := grpc.NewServer()
|
||||||
|
|
||||||
|
b.serviceImpl = svc.NewServer(config)
|
||||||
|
b.serviceImpl.Register(server)
|
||||||
|
|
||||||
|
pipe := internal.NewPipeListener()
|
||||||
|
go server.Serve(pipe)
|
||||||
|
t.Cleanup(server.Stop)
|
||||||
|
|
||||||
|
conn, err := grpc.Dial("",
|
||||||
|
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||||
|
grpc.WithContextDialer(pipe.DialContext),
|
||||||
|
grpc.WithBlock(),
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
t.Cleanup(func() { _ = conn.Close() })
|
||||||
|
|
||||||
|
client := pbresource.NewResourceServiceClient(conn)
|
||||||
|
|
||||||
|
// HACK ALERT: The client needs to be injected into the V2TenancyBridge
|
||||||
|
// after it has been created due the the circular dependency. This will
|
||||||
|
// go away when the tenancy bridge is removed and V1 is no more, however
|
||||||
|
// long that takes.
|
||||||
|
switch config.TenancyBridge.(type) {
|
||||||
|
case *tenancy.V2TenancyBridge:
|
||||||
|
config.TenancyBridge.(*tenancy.V2TenancyBridge).WithClient(client)
|
||||||
|
// Default partition namespace can finally be created
|
||||||
|
require.NoError(t, initTenancy(ctx, backend))
|
||||||
|
|
||||||
|
for _, tenancy := range b.tenancies {
|
||||||
|
if tenancy.Partition == resource.DefaultPartitionName && tenancy.Namespace == resource.DefaultNamespaceName {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t.Fatalf("TODO: implement creation of passed in v2 tenancy: %v", tenancy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return client
|
||||||
|
}
|
|
@ -4,27 +4,15 @@
|
||||||
package testing
|
package testing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hashicorp/go-uuid"
|
|
||||||
"github.com/stretchr/testify/mock"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"google.golang.org/grpc"
|
|
||||||
"google.golang.org/grpc/credentials/insecure"
|
"github.com/hashicorp/go-uuid"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/acl"
|
"github.com/hashicorp/consul/acl"
|
||||||
"github.com/hashicorp/consul/acl/resolver"
|
"github.com/hashicorp/consul/acl/resolver"
|
||||||
svc "github.com/hashicorp/consul/agent/grpc-external/services/resource"
|
|
||||||
"github.com/hashicorp/consul/agent/grpc-external/testutils"
|
|
||||||
internal "github.com/hashicorp/consul/agent/grpc-internal"
|
|
||||||
"github.com/hashicorp/consul/agent/structs"
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
"github.com/hashicorp/consul/internal/resource"
|
|
||||||
rtest "github.com/hashicorp/consul/internal/resource/resourcetest"
|
|
||||||
"github.com/hashicorp/consul/internal/storage/inmem"
|
|
||||||
"github.com/hashicorp/consul/internal/tenancy"
|
|
||||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
|
||||||
"github.com/hashicorp/consul/sdk/testutil"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func randomACLIdentity(t *testing.T) structs.ACLIdentity {
|
func randomACLIdentity(t *testing.T) structs.ACLIdentity {
|
||||||
|
@ -50,129 +38,3 @@ func AuthorizerFrom(t *testing.T, policyStrs ...string) resolver.Result {
|
||||||
ACLIdentity: randomACLIdentity(t),
|
ACLIdentity: randomACLIdentity(t),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunResourceService runs a Resource Service for the duration of the test and
|
|
||||||
// returns a client to interact with it. ACLs will be disabled and only the
|
|
||||||
// default partition and namespace are available.
|
|
||||||
func RunResourceService(t *testing.T, registerFns ...func(resource.Registry)) pbresource.ResourceServiceClient {
|
|
||||||
return RunResourceServiceWithConfig(t, svc.Config{}, registerFns...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunResourceServiceWithTenancies runs a Resource Service with tenancies returned from TestTenancies.
|
|
||||||
func RunResourceServiceWithTenancies(t *testing.T, registerFns ...func(resource.Registry)) pbresource.ResourceServiceClient {
|
|
||||||
mockTenancyBridge := &svc.MockTenancyBridge{}
|
|
||||||
|
|
||||||
for _, tenant := range rtest.TestTenancies() {
|
|
||||||
mockTenancyBridge.On("PartitionExists", tenant.Partition).Return(true, nil)
|
|
||||||
mockTenancyBridge.On("NamespaceExists", tenant.Partition, tenant.Namespace).Return(true, nil)
|
|
||||||
mockTenancyBridge.On("IsPartitionMarkedForDeletion", tenant.Partition).Return(false, nil)
|
|
||||||
mockTenancyBridge.On("IsNamespaceMarkedForDeletion", tenant.Partition, tenant.Namespace).Return(false, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg := &svc.Config{
|
|
||||||
TenancyBridge: mockTenancyBridge,
|
|
||||||
}
|
|
||||||
return RunResourceServiceWithConfig(t, *cfg, registerFns...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunResourceServiceWithConfig runs a ResourceService with caller injectable config to ease mocking dependencies.
|
|
||||||
// Any nil config field is replaced with a reasonable default with the following behavior:
|
|
||||||
//
|
|
||||||
// config.Backend - cannot be configured and must be nil
|
|
||||||
// config.Registry - empty registry
|
|
||||||
// config.TenancyBridge - mock provided with only the default partition and namespace
|
|
||||||
// config.ACLResolver - mock provided with ACLs disabled. Fills entMeta and authzContext with default partition and namespace
|
|
||||||
func RunResourceServiceWithConfig(t *testing.T, config svc.Config, registerFns ...func(resource.Registry)) pbresource.ResourceServiceClient {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
if config.Backend != nil {
|
|
||||||
panic("backend can not be configured")
|
|
||||||
}
|
|
||||||
|
|
||||||
backend, err := inmem.NewBackend()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
t.Cleanup(cancel)
|
|
||||||
go backend.Run(ctx)
|
|
||||||
config.Backend = backend
|
|
||||||
|
|
||||||
if config.Registry == nil {
|
|
||||||
config.Registry = resource.NewRegistry()
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, fn := range registerFns {
|
|
||||||
fn(config.Registry)
|
|
||||||
}
|
|
||||||
|
|
||||||
server := grpc.NewServer()
|
|
||||||
|
|
||||||
if config.TenancyBridge == nil {
|
|
||||||
mockTenancyBridge := &svc.MockTenancyBridge{}
|
|
||||||
mockTenancyBridge.On("PartitionExists", resource.DefaultPartitionName).Return(true, nil)
|
|
||||||
mockTenancyBridge.On("PartitionExists", "foo").Return(true, nil)
|
|
||||||
mockTenancyBridge.On("NamespaceExists", resource.DefaultPartitionName, resource.DefaultNamespaceName).Return(true, nil)
|
|
||||||
mockTenancyBridge.On("PartitionExists", "foo").Return(true, nil)
|
|
||||||
mockTenancyBridge.On("IsPartitionMarkedForDeletion", resource.DefaultPartitionName).Return(false, nil)
|
|
||||||
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 {
|
|
||||||
// Provide a resolver which will default partition and namespace when not provided. This is similar to user
|
|
||||||
// initiated requests.
|
|
||||||
//
|
|
||||||
// Controllers under test should be providing full tenancy since they will run with the DANGER_NO_AUTH.
|
|
||||||
mockACLResolver := &svc.MockACLResolver{}
|
|
||||||
mockACLResolver.On("ResolveTokenAndDefaultMeta", mock.Anything, mock.Anything, mock.Anything).
|
|
||||||
Return(testutils.ACLsDisabled(t), nil).
|
|
||||||
Run(func(args mock.Arguments) {
|
|
||||||
// Caller expecting passed in tokenEntMeta and authorizerContext to be filled in.
|
|
||||||
tokenEntMeta := args.Get(1).(*acl.EnterpriseMeta)
|
|
||||||
if tokenEntMeta != nil {
|
|
||||||
FillEntMeta(tokenEntMeta)
|
|
||||||
}
|
|
||||||
|
|
||||||
authzContext := args.Get(2).(*acl.AuthorizerContext)
|
|
||||||
if authzContext != nil {
|
|
||||||
FillAuthorizerContext(authzContext)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
config.ACLResolver = mockACLResolver
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.Logger == nil {
|
|
||||||
config.Logger = testutil.Logger(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
svc.NewServer(config).Register(server)
|
|
||||||
|
|
||||||
pipe := internal.NewPipeListener()
|
|
||||||
go server.Serve(pipe)
|
|
||||||
t.Cleanup(server.Stop)
|
|
||||||
|
|
||||||
conn, err := grpc.Dial("",
|
|
||||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
|
||||||
grpc.WithContextDialer(pipe.DialContext),
|
|
||||||
grpc.WithBlock(),
|
|
||||||
)
|
|
||||||
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 client
|
|
||||||
}
|
|
||||||
|
|
|
@ -31,8 +31,6 @@ func FillAuthorizerContext(authzContext *acl.AuthorizerContext) {
|
||||||
|
|
||||||
// initTenancy create the base tenancy objects (default/default)
|
// initTenancy create the base tenancy objects (default/default)
|
||||||
func initTenancy(ctx context.Context, b *inmem.Backend) error {
|
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"})
|
nsData, err := anypb.New(&pbtenancy.Namespace{Description: "default namespace in default partition"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -61,5 +59,4 @@ func initTenancy(ctx context.Context, b *inmem.Backend) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright (c) HashiCorp, Inc.
|
// Copyright (c) HashiCorp, Inc.
|
||||||
// SPDX-License-Identifier: BUSL-1.1
|
// SPDX-License-Identifier: BUSL-1.1
|
||||||
|
|
||||||
package resource
|
package resource_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -11,24 +11,28 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/acl"
|
|
||||||
"github.com/hashicorp/consul/agent/grpc-external/testutils"
|
|
||||||
"github.com/hashicorp/consul/internal/resource"
|
|
||||||
"github.com/hashicorp/consul/internal/resource/demo"
|
|
||||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
|
||||||
"github.com/hashicorp/consul/proto/private/prototest"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/acl"
|
||||||
|
svc "github.com/hashicorp/consul/agent/grpc-external/services/resource"
|
||||||
|
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
|
||||||
|
"github.com/hashicorp/consul/agent/grpc-external/testutils"
|
||||||
|
"github.com/hashicorp/consul/internal/resource"
|
||||||
|
"github.com/hashicorp/consul/internal/resource/demo"
|
||||||
|
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||||
|
"github.com/hashicorp/consul/proto/private/prototest"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO: Update all tests to use true/false table test for v2tenancy
|
||||||
|
|
||||||
func TestWatchList_InputValidation(t *testing.T) {
|
func TestWatchList_InputValidation(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
demo.RegisterTypes(server.Registry)
|
Run(t)
|
||||||
|
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
modFn func(*pbresource.WatchListRequest)
|
modFn func(*pbresource.WatchListRequest)
|
||||||
|
@ -108,8 +112,7 @@ func TestWatchList_InputValidation(t *testing.T) {
|
||||||
func TestWatchList_TypeNotFound(t *testing.T) {
|
func TestWatchList_TypeNotFound(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().Run(t)
|
||||||
client := testClient(t, server)
|
|
||||||
|
|
||||||
stream, err := client.WatchList(context.Background(), &pbresource.WatchListRequest{
|
stream, err := client.WatchList(context.Background(), &pbresource.WatchListRequest{
|
||||||
Type: demo.TypeV2Artist,
|
Type: demo.TypeV2Artist,
|
||||||
|
@ -171,9 +174,9 @@ func TestWatchList_Tenancy_Defaults_And_Normalization(t *testing.T) {
|
||||||
for desc, tc := range wildcardTenancyCases() {
|
for desc, tc := range wildcardTenancyCases() {
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
demo.RegisterTypes(server.Registry)
|
Run(t)
|
||||||
|
|
||||||
// Create a watch.
|
// Create a watch.
|
||||||
stream, err := client.WatchList(ctx, &pbresource.WatchListRequest{
|
stream, err := client.WatchList(ctx, &pbresource.WatchListRequest{
|
||||||
|
@ -191,17 +194,17 @@ func TestWatchList_Tenancy_Defaults_And_Normalization(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Create and verify upsert event received.
|
// Create and verify upsert event received.
|
||||||
recordLabel, err = server.Backend.WriteCAS(ctx, recordLabel)
|
rlRsp, err := client.Write(ctx, &pbresource.WriteRequest{Resource: recordLabel})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
artist, err = server.Backend.WriteCAS(ctx, artist)
|
artistRsp, err := client.Write(ctx, &pbresource.WriteRequest{Resource: artist})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var expected *pbresource.Resource
|
var expected *pbresource.Resource
|
||||||
switch {
|
switch {
|
||||||
case proto.Equal(tc.typ, demo.TypeV1RecordLabel):
|
case proto.Equal(tc.typ, demo.TypeV1RecordLabel):
|
||||||
expected = recordLabel
|
expected = rlRsp.Resource
|
||||||
case proto.Equal(tc.typ, demo.TypeV2Artist):
|
case proto.Equal(tc.typ, demo.TypeV2Artist):
|
||||||
expected = artist
|
expected = artistRsp.Resource
|
||||||
default:
|
default:
|
||||||
require.Fail(t, "unsupported type", tc.typ)
|
require.Fail(t, "unsupported type", tc.typ)
|
||||||
}
|
}
|
||||||
|
@ -210,7 +213,6 @@ func TestWatchList_Tenancy_Defaults_And_Normalization(t *testing.T) {
|
||||||
require.Equal(t, pbresource.WatchEvent_OPERATION_UPSERT, rsp.Operation)
|
require.Equal(t, pbresource.WatchEvent_OPERATION_UPSERT, rsp.Operation)
|
||||||
prototest.AssertDeepEqual(t, expected, rsp.Resource)
|
prototest.AssertDeepEqual(t, expected, rsp.Resource)
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,7 +306,7 @@ func roundTripACL(t *testing.T, authz acl.Authorizer) (<-chan resourceOrError, *
|
||||||
server := testServer(t)
|
server := testServer(t)
|
||||||
client := testClient(t, server)
|
client := testClient(t, server)
|
||||||
|
|
||||||
mockACLResolver := &MockACLResolver{}
|
mockACLResolver := &svc.MockACLResolver{}
|
||||||
mockACLResolver.On("ResolveTokenAndDefaultMeta", mock.Anything, mock.Anything, mock.Anything).
|
mockACLResolver.On("ResolveTokenAndDefaultMeta", mock.Anything, mock.Anything, mock.Anything).
|
||||||
Return(authz, nil)
|
Return(authz, nil)
|
||||||
server.ACLResolver = mockACLResolver
|
server.ACLResolver = mockACLResolver
|
||||||
|
@ -395,10 +397,11 @@ type resourceOrError struct {
|
||||||
|
|
||||||
func TestWatchList_NoTenancy(t *testing.T) {
|
func TestWatchList_NoTenancy(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
demo.RegisterTypes(server.Registry)
|
Run(t)
|
||||||
|
|
||||||
// Create a watch.
|
// Create a watch.
|
||||||
stream, err := client.WatchList(ctx, &pbresource.WatchListRequest{
|
stream, err := client.WatchList(ctx, &pbresource.WatchListRequest{
|
||||||
|
@ -411,11 +414,11 @@ func TestWatchList_NoTenancy(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Create and verify upsert event received.
|
// Create and verify upsert event received.
|
||||||
recordLabel, err = server.Backend.WriteCAS(ctx, recordLabel)
|
rsp1, err := client.Write(ctx, &pbresource.WriteRequest{Resource: recordLabel})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
rsp := mustGetResource(t, rspCh)
|
rsp2 := mustGetResource(t, rspCh)
|
||||||
|
|
||||||
require.Equal(t, pbresource.WatchEvent_OPERATION_UPSERT, rsp.Operation)
|
require.Equal(t, pbresource.WatchEvent_OPERATION_UPSERT, rsp2.Operation)
|
||||||
prototest.AssertDeepEqual(t, recordLabel, rsp.Resource)
|
prototest.AssertDeepEqual(t, rsp1.Resource, rsp2.Resource)
|
||||||
}
|
}
|
||||||
|
|
|
@ -357,8 +357,9 @@ func ensureDataUnchanged(input *pbresource.Resource, existing *pbresource.Resour
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensureFinalizerRemoved ensures at least one finalizer was removed.
|
// EnsureFinalizerRemoved ensures at least one finalizer was removed.
|
||||||
func ensureFinalizerRemoved(input *pbresource.Resource, existing *pbresource.Resource) error {
|
// TODO: only public for test to access
|
||||||
|
func EnsureFinalizerRemoved(input *pbresource.Resource, existing *pbresource.Resource) error {
|
||||||
inputFinalizers := resource.GetFinalizers(input)
|
inputFinalizers := resource.GetFinalizers(input)
|
||||||
existingFinalizers := resource.GetFinalizers(existing)
|
existingFinalizers := resource.GetFinalizers(existing)
|
||||||
if !inputFinalizers.IsProperSubset(existingFinalizers) {
|
if !inputFinalizers.IsProperSubset(existingFinalizers) {
|
||||||
|
@ -419,13 +420,13 @@ func vetIfDeleteRelated(input, existing *pbresource.Resource, tenancyMarkedForDe
|
||||||
if err := ensureDataUnchanged(input, existing); err != nil {
|
if err := ensureDataUnchanged(input, existing); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := ensureFinalizerRemoved(input, existing); err != nil {
|
if err := EnsureFinalizerRemoved(input, existing); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Classify writes that just remove finalizer as deleteRelated regardless of deletion state.
|
// Classify writes that just remove finalizer as deleteRelated regardless of deletion state.
|
||||||
if err := ensureFinalizerRemoved(input, existing); err == nil {
|
if err := EnsureFinalizerRemoved(input, existing); err == nil {
|
||||||
if err := ensureDataUnchanged(input, existing); err == nil {
|
if err := ensureDataUnchanged(input, existing); err == nil {
|
||||||
deleteRelated = deleteRelated || true
|
deleteRelated = deleteRelated || true
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright (c) HashiCorp, Inc.
|
// Copyright (c) HashiCorp, Inc.
|
||||||
// SPDX-License-Identifier: BUSL-1.1
|
// SPDX-License-Identifier: BUSL-1.1
|
||||||
|
|
||||||
package resource
|
package resource_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -16,11 +16,15 @@ import (
|
||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/acl/resolver"
|
"github.com/hashicorp/consul/acl/resolver"
|
||||||
|
svc "github.com/hashicorp/consul/agent/grpc-external/services/resource"
|
||||||
|
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
|
||||||
"github.com/hashicorp/consul/internal/resource"
|
"github.com/hashicorp/consul/internal/resource"
|
||||||
"github.com/hashicorp/consul/internal/resource/demo"
|
"github.com/hashicorp/consul/internal/resource/demo"
|
||||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO: Update all tests to use true/false table test for v2tenancy
|
||||||
|
|
||||||
func TestWriteStatus_ACL(t *testing.T) {
|
func TestWriteStatus_ACL(t *testing.T) {
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
authz resolver.Result
|
authz resolver.Result
|
||||||
|
@ -44,9 +48,8 @@ func TestWriteStatus_ACL(t *testing.T) {
|
||||||
|
|
||||||
for desc, tc := range testcases {
|
for desc, tc := range testcases {
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
server := testServer(t)
|
builder := svctest.NewResourceServiceBuilder().WithRegisterFns(demo.RegisterTypes)
|
||||||
client := testClient(t, server)
|
client := builder.Run(t)
|
||||||
demo.RegisterTypes(server.Registry)
|
|
||||||
|
|
||||||
artist, err := demo.GenerateV2Artist()
|
artist, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -56,10 +59,10 @@ func TestWriteStatus_ACL(t *testing.T) {
|
||||||
artist = rsp.Resource
|
artist = rsp.Resource
|
||||||
|
|
||||||
// Defer mocking out authz since above write is necessary to set up the test resource.
|
// Defer mocking out authz since above write is necessary to set up the test resource.
|
||||||
mockACLResolver := &MockACLResolver{}
|
mockACLResolver := &svc.MockACLResolver{}
|
||||||
mockACLResolver.On("ResolveTokenAndDefaultMeta", mock.Anything, mock.Anything, mock.Anything).
|
mockACLResolver.On("ResolveTokenAndDefaultMeta", mock.Anything, mock.Anything, mock.Anything).
|
||||||
Return(tc.authz, nil)
|
Return(tc.authz, nil)
|
||||||
server.ACLResolver = mockACLResolver
|
builder.ServiceImpl().Config.ACLResolver = mockACLResolver
|
||||||
|
|
||||||
// exercise ACL
|
// exercise ACL
|
||||||
_, err = client.WriteStatus(testContext(t), validWriteStatusRequest(t, artist))
|
_, err = client.WriteStatus(testContext(t), validWriteStatusRequest(t, artist))
|
||||||
|
@ -69,9 +72,9 @@ func TestWriteStatus_ACL(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWriteStatus_InputValidation(t *testing.T) {
|
func TestWriteStatus_InputValidation(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
demo.RegisterTypes(server.Registry)
|
Run(t)
|
||||||
|
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
typ *pbresource.Type
|
typ *pbresource.Type
|
||||||
|
@ -259,9 +262,9 @@ func TestWriteStatus_Success(t *testing.T) {
|
||||||
"Non CAS": func(req *pbresource.WriteStatusRequest) { req.Version = "" },
|
"Non CAS": func(req *pbresource.WriteStatusRequest) { req.Version = "" },
|
||||||
} {
|
} {
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
demo.RegisterTypes(server.Registry)
|
Run(t)
|
||||||
|
|
||||||
res, err := demo.GenerateV2Artist()
|
res, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -331,9 +334,9 @@ func TestWriteStatus_Tenancy_Defaults(t *testing.T) {
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
demo.RegisterTypes(server.Registry)
|
Run(t)
|
||||||
|
|
||||||
// Pick resource based on scope of type in testcase.
|
// Pick resource based on scope of type in testcase.
|
||||||
var res *pbresource.Resource
|
var res *pbresource.Resource
|
||||||
|
@ -395,9 +398,10 @@ func TestWriteStatus_Tenancy_NotFound(t *testing.T) {
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithV2Tenancy(true).
|
||||||
demo.RegisterTypes(server.Registry)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
|
|
||||||
// Pick resource based on scope of type in testcase.
|
// Pick resource based on scope of type in testcase.
|
||||||
var res *pbresource.Resource
|
var res *pbresource.Resource
|
||||||
|
@ -428,10 +432,9 @@ func TestWriteStatus_Tenancy_NotFound(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWriteStatus_CASFailure(t *testing.T) {
|
func TestWriteStatus_CASFailure(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
demo.RegisterTypes(server.Registry)
|
|
||||||
|
|
||||||
res, err := demo.GenerateV2Artist()
|
res, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -449,8 +452,7 @@ func TestWriteStatus_CASFailure(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWriteStatus_TypeNotFound(t *testing.T) {
|
func TestWriteStatus_TypeNotFound(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().Run(t)
|
||||||
client := testClient(t, server)
|
|
||||||
|
|
||||||
res, err := demo.GenerateV2Artist()
|
res, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -464,9 +466,9 @@ func TestWriteStatus_TypeNotFound(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWriteStatus_ResourceNotFound(t *testing.T) {
|
func TestWriteStatus_ResourceNotFound(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
demo.RegisterTypes(server.Registry)
|
Run(t)
|
||||||
|
|
||||||
res, err := demo.GenerateV2Artist()
|
res, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -479,9 +481,9 @@ func TestWriteStatus_ResourceNotFound(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWriteStatus_WrongUid(t *testing.T) {
|
func TestWriteStatus_WrongUid(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
demo.RegisterTypes(server.Registry)
|
Run(t)
|
||||||
|
|
||||||
res, err := demo.GenerateV2Artist()
|
res, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright (c) HashiCorp, Inc.
|
// Copyright (c) HashiCorp, Inc.
|
||||||
// SPDX-License-Identifier: BUSL-1.1
|
// SPDX-License-Identifier: BUSL-1.1
|
||||||
|
|
||||||
package resource
|
package resource_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -18,6 +18,8 @@ import (
|
||||||
"google.golang.org/protobuf/types/known/anypb"
|
"google.golang.org/protobuf/types/known/anypb"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/acl/resolver"
|
"github.com/hashicorp/consul/acl/resolver"
|
||||||
|
svc "github.com/hashicorp/consul/agent/grpc-external/services/resource"
|
||||||
|
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
|
||||||
"github.com/hashicorp/consul/internal/resource"
|
"github.com/hashicorp/consul/internal/resource"
|
||||||
"github.com/hashicorp/consul/internal/resource/demo"
|
"github.com/hashicorp/consul/internal/resource/demo"
|
||||||
rtest "github.com/hashicorp/consul/internal/resource/resourcetest"
|
rtest "github.com/hashicorp/consul/internal/resource/resourcetest"
|
||||||
|
@ -29,10 +31,12 @@ import (
|
||||||
"github.com/hashicorp/consul/proto/private/prototest"
|
"github.com/hashicorp/consul/proto/private/prototest"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO: Update all tests to use true/false table test for v2tenancy
|
||||||
|
|
||||||
func TestWrite_InputValidation(t *testing.T) {
|
func TestWrite_InputValidation(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
demo.RegisterTypes(server.Registry)
|
Run(t)
|
||||||
|
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
modFn func(artist, recordLabel *pbresource.Resource) *pbresource.Resource
|
modFn func(artist, recordLabel *pbresource.Resource) *pbresource.Resource
|
||||||
|
@ -154,9 +158,9 @@ func TestWrite_InputValidation(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWrite_OwnerValidation(t *testing.T) {
|
func TestWrite_OwnerValidation(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
demo.RegisterTypes(server.Registry)
|
Run(t)
|
||||||
|
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
modReqFn func(req *pbresource.WriteRequest)
|
modReqFn func(req *pbresource.WriteRequest)
|
||||||
|
@ -230,8 +234,7 @@ func TestWrite_OwnerValidation(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWrite_TypeNotFound(t *testing.T) {
|
func TestWrite_TypeNotFound(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().Run(t)
|
||||||
client := testClient(t, server)
|
|
||||||
|
|
||||||
res, err := demo.GenerateV2Artist()
|
res, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -265,14 +268,14 @@ func TestWrite_ACLs(t *testing.T) {
|
||||||
|
|
||||||
for desc, tc := range testcases {
|
for desc, tc := range testcases {
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
server := testServer(t)
|
mockACLResolver := &svc.MockACLResolver{}
|
||||||
client := testClient(t, server)
|
|
||||||
|
|
||||||
mockACLResolver := &MockACLResolver{}
|
|
||||||
mockACLResolver.On("ResolveTokenAndDefaultMeta", mock.Anything, mock.Anything, mock.Anything).
|
mockACLResolver.On("ResolveTokenAndDefaultMeta", mock.Anything, mock.Anything, mock.Anything).
|
||||||
Return(tc.authz, nil)
|
Return(tc.authz, nil)
|
||||||
server.ACLResolver = mockACLResolver
|
|
||||||
demo.RegisterTypes(server.Registry)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
WithACLResolver(mockACLResolver).
|
||||||
|
Run(t)
|
||||||
|
|
||||||
artist, err := demo.GenerateV2Artist()
|
artist, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -285,9 +288,9 @@ func TestWrite_ACLs(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWrite_Mutate(t *testing.T) {
|
func TestWrite_Mutate(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
demo.RegisterTypes(server.Registry)
|
Run(t)
|
||||||
|
|
||||||
artist, err := demo.GenerateV2Artist()
|
artist, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -389,9 +392,9 @@ func TestWrite_Create_Success(t *testing.T) {
|
||||||
}
|
}
|
||||||
for desc, tc := range testCases {
|
for desc, tc := range testCases {
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
demo.RegisterTypes(server.Registry)
|
Run(t)
|
||||||
|
|
||||||
recordLabel, err := demo.GenerateV1RecordLabel("looney-tunes")
|
recordLabel, err := demo.GenerateV1RecordLabel("looney-tunes")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -442,9 +445,10 @@ func TestWrite_Create_Tenancy_NotFound(t *testing.T) {
|
||||||
}
|
}
|
||||||
for desc, tc := range testCases {
|
for desc, tc := range testCases {
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithV2Tenancy(true).
|
||||||
demo.RegisterTypes(server.Registry)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
|
|
||||||
recordLabel, err := demo.GenerateV1RecordLabel("looney-tunes")
|
recordLabel, err := demo.GenerateV1RecordLabel("looney-tunes")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -461,9 +465,10 @@ func TestWrite_Create_Tenancy_NotFound(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWrite_Create_With_DeletionTimestamp_Fails(t *testing.T) {
|
func TestWrite_Create_With_DeletionTimestamp_Fails(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithV2Tenancy(true).
|
||||||
demo.RegisterTypes(server.Registry)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
|
|
||||||
res := rtest.Resource(demo.TypeV1Artist, "blur").
|
res := rtest.Resource(demo.TypeV1Artist, "blur").
|
||||||
WithTenancy(resource.DefaultNamespacedTenancy()).
|
WithTenancy(resource.DefaultNamespacedTenancy()).
|
||||||
|
@ -480,18 +485,18 @@ func TestWrite_Create_With_DeletionTimestamp_Fails(t *testing.T) {
|
||||||
func TestWrite_Create_With_TenancyMarkedForDeletion_Fails(t *testing.T) {
|
func TestWrite_Create_With_TenancyMarkedForDeletion_Fails(t *testing.T) {
|
||||||
// Verify resource write fails when its partition or namespace is marked for deletion.
|
// Verify resource write fails when its partition or namespace is marked for deletion.
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
modFn func(artist, recordLabel *pbresource.Resource, mockTenancyBridge *MockTenancyBridge) *pbresource.Resource
|
modFn func(artist, recordLabel *pbresource.Resource, mockTenancyBridge *svc.MockTenancyBridge) *pbresource.Resource
|
||||||
errContains string
|
errContains string
|
||||||
}{
|
}{
|
||||||
"namespaced resources partition marked for deletion": {
|
"namespaced resources partition marked for deletion": {
|
||||||
modFn: func(artist, _ *pbresource.Resource, mockTenancyBridge *MockTenancyBridge) *pbresource.Resource {
|
modFn: func(artist, _ *pbresource.Resource, mockTenancyBridge *svc.MockTenancyBridge) *pbresource.Resource {
|
||||||
mockTenancyBridge.On("IsPartitionMarkedForDeletion", "ap1").Return(true, nil)
|
mockTenancyBridge.On("IsPartitionMarkedForDeletion", "ap1").Return(true, nil)
|
||||||
return artist
|
return artist
|
||||||
},
|
},
|
||||||
errContains: "tenancy marked for deletion",
|
errContains: "tenancy marked for deletion",
|
||||||
},
|
},
|
||||||
"namespaced resources namespace marked for deletion": {
|
"namespaced resources namespace marked for deletion": {
|
||||||
modFn: func(artist, _ *pbresource.Resource, mockTenancyBridge *MockTenancyBridge) *pbresource.Resource {
|
modFn: func(artist, _ *pbresource.Resource, mockTenancyBridge *svc.MockTenancyBridge) *pbresource.Resource {
|
||||||
mockTenancyBridge.On("IsPartitionMarkedForDeletion", "ap1").Return(false, nil)
|
mockTenancyBridge.On("IsPartitionMarkedForDeletion", "ap1").Return(false, nil)
|
||||||
mockTenancyBridge.On("IsNamespaceMarkedForDeletion", "ap1", "ns1").Return(true, nil)
|
mockTenancyBridge.On("IsNamespaceMarkedForDeletion", "ap1", "ns1").Return(true, nil)
|
||||||
return artist
|
return artist
|
||||||
|
@ -499,7 +504,7 @@ func TestWrite_Create_With_TenancyMarkedForDeletion_Fails(t *testing.T) {
|
||||||
errContains: "tenancy marked for deletion",
|
errContains: "tenancy marked for deletion",
|
||||||
},
|
},
|
||||||
"partitioned resources partition marked for deletion": {
|
"partitioned resources partition marked for deletion": {
|
||||||
modFn: func(_, recordLabel *pbresource.Resource, mockTenancyBridge *MockTenancyBridge) *pbresource.Resource {
|
modFn: func(_, recordLabel *pbresource.Resource, mockTenancyBridge *svc.MockTenancyBridge) *pbresource.Resource {
|
||||||
mockTenancyBridge.On("IsPartitionMarkedForDeletion", "ap1").Return(true, nil)
|
mockTenancyBridge.On("IsPartitionMarkedForDeletion", "ap1").Return(true, nil)
|
||||||
return recordLabel
|
return recordLabel
|
||||||
},
|
},
|
||||||
|
@ -511,6 +516,7 @@ func TestWrite_Create_With_TenancyMarkedForDeletion_Fails(t *testing.T) {
|
||||||
server := testServer(t)
|
server := testServer(t)
|
||||||
client := testClient(t, server)
|
client := testClient(t, server)
|
||||||
demo.RegisterTypes(server.Registry)
|
demo.RegisterTypes(server.Registry)
|
||||||
|
|
||||||
recordLabel, err := demo.GenerateV1RecordLabel("looney-tunes")
|
recordLabel, err := demo.GenerateV1RecordLabel("looney-tunes")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
recordLabel.Id.Tenancy.Partition = "ap1"
|
recordLabel.Id.Tenancy.Partition = "ap1"
|
||||||
|
@ -520,7 +526,7 @@ func TestWrite_Create_With_TenancyMarkedForDeletion_Fails(t *testing.T) {
|
||||||
artist.Id.Tenancy.Partition = "ap1"
|
artist.Id.Tenancy.Partition = "ap1"
|
||||||
artist.Id.Tenancy.Namespace = "ns1"
|
artist.Id.Tenancy.Namespace = "ns1"
|
||||||
|
|
||||||
mockTenancyBridge := &MockTenancyBridge{}
|
mockTenancyBridge := &svc.MockTenancyBridge{}
|
||||||
mockTenancyBridge.On("PartitionExists", "ap1").Return(true, nil)
|
mockTenancyBridge.On("PartitionExists", "ap1").Return(true, nil)
|
||||||
mockTenancyBridge.On("NamespaceExists", "ap1", "ns1").Return(true, nil)
|
mockTenancyBridge.On("NamespaceExists", "ap1", "ns1").Return(true, nil)
|
||||||
server.TenancyBridge = mockTenancyBridge
|
server.TenancyBridge = mockTenancyBridge
|
||||||
|
@ -534,10 +540,9 @@ func TestWrite_Create_With_TenancyMarkedForDeletion_Fails(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWrite_CASUpdate_Success(t *testing.T) {
|
func TestWrite_CASUpdate_Success(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
demo.RegisterTypes(server.Registry)
|
|
||||||
|
|
||||||
res, err := demo.GenerateV2Artist()
|
res, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -556,10 +561,9 @@ func TestWrite_CASUpdate_Success(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWrite_ResourceCreation_StatusProvided(t *testing.T) {
|
func TestWrite_ResourceCreation_StatusProvided(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
demo.RegisterTypes(server.Registry)
|
|
||||||
|
|
||||||
res, err := demo.GenerateV2Artist()
|
res, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -575,10 +579,9 @@ func TestWrite_ResourceCreation_StatusProvided(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWrite_CASUpdate_Failure(t *testing.T) {
|
func TestWrite_CASUpdate_Failure(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
demo.RegisterTypes(server.Registry)
|
|
||||||
|
|
||||||
res, err := demo.GenerateV2Artist()
|
res, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -596,10 +599,9 @@ func TestWrite_CASUpdate_Failure(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWrite_Update_WrongUid(t *testing.T) {
|
func TestWrite_Update_WrongUid(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
demo.RegisterTypes(server.Registry)
|
|
||||||
|
|
||||||
res, err := demo.GenerateV2Artist()
|
res, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -617,10 +619,9 @@ func TestWrite_Update_WrongUid(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWrite_Update_StatusModified(t *testing.T) {
|
func TestWrite_Update_StatusModified(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
demo.RegisterTypes(server.Registry)
|
|
||||||
|
|
||||||
res, err := demo.GenerateV2Artist()
|
res, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -647,10 +648,9 @@ func TestWrite_Update_StatusModified(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWrite_Update_NilStatus(t *testing.T) {
|
func TestWrite_Update_NilStatus(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
demo.RegisterTypes(server.Registry)
|
|
||||||
|
|
||||||
res, err := demo.GenerateV2Artist()
|
res, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -671,10 +671,9 @@ func TestWrite_Update_NilStatus(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWrite_Update_NoUid(t *testing.T) {
|
func TestWrite_Update_NoUid(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
demo.RegisterTypes(server.Registry)
|
|
||||||
|
|
||||||
res, err := demo.GenerateV2Artist()
|
res, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -690,10 +689,9 @@ func TestWrite_Update_NoUid(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWrite_Update_GroupVersion(t *testing.T) {
|
func TestWrite_Update_GroupVersion(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
demo.RegisterTypes(server.Registry)
|
|
||||||
|
|
||||||
res, err := demo.GenerateV2Artist()
|
res, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -720,10 +718,9 @@ func TestWrite_Update_GroupVersion(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWrite_NonCASUpdate_Success(t *testing.T) {
|
func TestWrite_NonCASUpdate_Success(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
demo.RegisterTypes(server.Registry)
|
|
||||||
|
|
||||||
res, err := demo.GenerateV2Artist()
|
res, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -743,7 +740,6 @@ func TestWrite_NonCASUpdate_Success(t *testing.T) {
|
||||||
func TestWrite_NonCASUpdate_Retry(t *testing.T) {
|
func TestWrite_NonCASUpdate_Retry(t *testing.T) {
|
||||||
server := testServer(t)
|
server := testServer(t)
|
||||||
client := testClient(t, server)
|
client := testClient(t, server)
|
||||||
|
|
||||||
demo.RegisterTypes(server.Registry)
|
demo.RegisterTypes(server.Registry)
|
||||||
|
|
||||||
res, err := demo.GenerateV2Artist()
|
res, err := demo.GenerateV2Artist()
|
||||||
|
@ -788,10 +784,9 @@ func TestWrite_NonCASUpdate_Retry(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWrite_NoData(t *testing.T) {
|
func TestWrite_NoData(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
demo.RegisterTypes(server.Registry)
|
|
||||||
|
|
||||||
res, err := demo.GenerateV1Concept("jazz")
|
res, err := demo.GenerateV1Concept("jazz")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -806,10 +801,9 @@ func TestWrite_Owner_Immutable(t *testing.T) {
|
||||||
// Use of proto.Equal(..) in implementation covers all permutations
|
// Use of proto.Equal(..) in implementation covers all permutations
|
||||||
// (nil -> non-nil, non-nil -> nil, owner1 -> owner2) so only the first one
|
// (nil -> non-nil, non-nil -> nil, owner1 -> owner2) so only the first one
|
||||||
// is tested.
|
// is tested.
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
demo.RegisterTypes(server.Registry)
|
|
||||||
|
|
||||||
artist, err := demo.GenerateV2Artist()
|
artist, err := demo.GenerateV2Artist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -834,10 +828,9 @@ func TestWrite_Owner_Immutable(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWrite_Owner_Uid(t *testing.T) {
|
func TestWrite_Owner_Uid(t *testing.T) {
|
||||||
server := testServer(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
client := testClient(t, server)
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
demo.RegisterTypes(server.Registry)
|
|
||||||
|
|
||||||
t.Run("uid given", func(t *testing.T) {
|
t.Run("uid given", func(t *testing.T) {
|
||||||
artist, err := demo.GenerateV2Artist()
|
artist, err := demo.GenerateV2Artist()
|
||||||
|
@ -991,7 +984,7 @@ func TestEnsureFinalizerRemoved(t *testing.T) {
|
||||||
|
|
||||||
tc.mod(input, existing)
|
tc.mod(input, existing)
|
||||||
|
|
||||||
err := ensureFinalizerRemoved(input, existing)
|
err := svc.EnsureFinalizerRemoved(input, existing)
|
||||||
if tc.errContains != "" {
|
if tc.errContains != "" {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.Equal(t, codes.InvalidArgument.String(), status.Code(err).String())
|
require.Equal(t, codes.InvalidArgument.String(), status.Code(err).String())
|
||||||
|
@ -1054,8 +1047,10 @@ func TestWrite_ResourceFrozenAfterMarkedForDeletion(t *testing.T) {
|
||||||
|
|
||||||
for desc, tc := range testCases {
|
for desc, tc := range testCases {
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
server, client, ctx := testDeps(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
demo.RegisterTypes(server.Registry)
|
WithV2Tenancy(true).
|
||||||
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
|
|
||||||
// Create a resource with finalizers
|
// Create a resource with finalizers
|
||||||
res := rtest.Resource(demo.TypeV1Artist, "joydivision").
|
res := rtest.Resource(demo.TypeV1Artist, "joydivision").
|
||||||
|
@ -1065,11 +1060,11 @@ func TestWrite_ResourceFrozenAfterMarkedForDeletion(t *testing.T) {
|
||||||
Write(t, client)
|
Write(t, client)
|
||||||
|
|
||||||
// Mark for deletion - resource should now be frozen
|
// Mark for deletion - resource should now be frozen
|
||||||
_, err := client.Delete(ctx, &pbresource.DeleteRequest{Id: res.Id})
|
_, err := client.Delete(context.Background(), &pbresource.DeleteRequest{Id: res.Id})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Verify marked for deletion
|
// Verify marked for deletion
|
||||||
rsp, err := client.Read(ctx, &pbresource.ReadRequest{Id: res.Id})
|
rsp, err := client.Read(context.Background(), &pbresource.ReadRequest{Id: res.Id})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.True(t, resource.IsMarkedForDeletion(rsp.Resource))
|
require.True(t, resource.IsMarkedForDeletion(rsp.Resource))
|
||||||
|
|
||||||
|
@ -1077,7 +1072,7 @@ func TestWrite_ResourceFrozenAfterMarkedForDeletion(t *testing.T) {
|
||||||
tc.modFn(rsp.Resource)
|
tc.modFn(rsp.Resource)
|
||||||
|
|
||||||
// Verify write results
|
// Verify write results
|
||||||
_, err = client.Write(ctx, &pbresource.WriteRequest{Resource: rsp.Resource})
|
_, err = client.Write(context.Background(), &pbresource.WriteRequest{Resource: rsp.Resource})
|
||||||
if tc.errContains == "" {
|
if tc.errContains == "" {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1120,8 +1115,10 @@ func TestWrite_NonCASWritePreservesFinalizers(t *testing.T) {
|
||||||
|
|
||||||
for desc, tc := range testCases {
|
for desc, tc := range testCases {
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
server, client, ctx := testDeps(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
demo.RegisterTypes(server.Registry)
|
WithV2Tenancy(true).
|
||||||
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
|
|
||||||
// Create the resource based on tc.existingMetadata
|
// Create the resource based on tc.existingMetadata
|
||||||
builder := rtest.Resource(demo.TypeV1Artist, "joydivision").
|
builder := rtest.Resource(demo.TypeV1Artist, "joydivision").
|
||||||
|
@ -1148,7 +1145,7 @@ func TestWrite_NonCASWritePreservesFinalizers(t *testing.T) {
|
||||||
userRes := builder.Build()
|
userRes := builder.Build()
|
||||||
|
|
||||||
// Perform the user write
|
// Perform the user write
|
||||||
rsp, err := client.Write(ctx, &pbresource.WriteRequest{Resource: userRes})
|
rsp, err := client.Write(context.Background(), &pbresource.WriteRequest{Resource: userRes})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Verify write result preserved metadata based on testcase.expecteMetadata
|
// Verify write result preserved metadata based on testcase.expecteMetadata
|
||||||
|
@ -1184,8 +1181,10 @@ func TestWrite_NonCASWritePreservesDeletionTimestamp(t *testing.T) {
|
||||||
|
|
||||||
for desc, tc := range testCases {
|
for desc, tc := range testCases {
|
||||||
t.Run(desc, func(t *testing.T) {
|
t.Run(desc, func(t *testing.T) {
|
||||||
server, client, ctx := testDeps(t)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
demo.RegisterTypes(server.Registry)
|
WithV2Tenancy(true).
|
||||||
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
|
|
||||||
// Create the resource based on tc.existingMetadata
|
// Create the resource based on tc.existingMetadata
|
||||||
builder := rtest.Resource(demo.TypeV1Artist, "joydivision").
|
builder := rtest.Resource(demo.TypeV1Artist, "joydivision").
|
||||||
|
@ -1200,11 +1199,11 @@ func TestWrite_NonCASWritePreservesDeletionTimestamp(t *testing.T) {
|
||||||
res := builder.Write(t, client)
|
res := builder.Write(t, client)
|
||||||
|
|
||||||
// Mark for deletion
|
// Mark for deletion
|
||||||
_, err := client.Delete(ctx, &pbresource.DeleteRequest{Id: res.Id})
|
_, err := client.Delete(context.Background(), &pbresource.DeleteRequest{Id: res.Id})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Re-read the deleted res for future comparison of deletionTimestamp
|
// Re-read the deleted res for future comparison of deletionTimestamp
|
||||||
delRsp, err := client.Read(ctx, &pbresource.ReadRequest{Id: res.Id})
|
delRsp, err := client.Read(context.Background(), &pbresource.ReadRequest{Id: res.Id})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Build resource for user write based on tc.inputMetadata
|
// Build resource for user write based on tc.inputMetadata
|
||||||
|
@ -1220,7 +1219,7 @@ func TestWrite_NonCASWritePreservesDeletionTimestamp(t *testing.T) {
|
||||||
userRes := builder.Build()
|
userRes := builder.Build()
|
||||||
|
|
||||||
// Perform the non-CAS user write
|
// Perform the non-CAS user write
|
||||||
rsp, err := client.Write(ctx, &pbresource.WriteRequest{Resource: userRes})
|
rsp, err := client.Write(context.Background(), &pbresource.WriteRequest{Resource: userRes})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Verify write result preserved metadata based on testcase.expecteMetadata
|
// Verify write result preserved metadata based on testcase.expecteMetadata
|
||||||
|
|
|
@ -41,7 +41,11 @@ func (suite *controllerSuite) SetupTest() {
|
||||||
suite.isEnterprise = versiontest.IsEnterprise()
|
suite.isEnterprise = versiontest.IsEnterprise()
|
||||||
suite.tenancies = resourcetest.TestTenancies()
|
suite.tenancies = resourcetest.TestTenancies()
|
||||||
suite.ctx = testutil.TestContext(suite.T())
|
suite.ctx = testutil.TestContext(suite.T())
|
||||||
client := svctest.RunResourceServiceWithTenancies(suite.T(), types.Register)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(types.Register).
|
||||||
|
WithTenancies(suite.tenancies...).
|
||||||
|
Run(suite.T())
|
||||||
|
|
||||||
suite.client = rtest.NewClient(client)
|
suite.client = rtest.NewClient(client)
|
||||||
suite.rt = controller.Runtime{
|
suite.rt = controller.Runtime{
|
||||||
Client: suite.client,
|
Client: suite.client,
|
||||||
|
|
|
@ -26,7 +26,9 @@ func runInMemResourceServiceAndControllers(t *testing.T, deps controllers.Depend
|
||||||
ctx := testutil.TestContext(t)
|
ctx := testutil.TestContext(t)
|
||||||
|
|
||||||
// Create the in-mem resource service
|
// Create the in-mem resource service
|
||||||
client := svctest.RunResourceService(t, catalog.RegisterTypes)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(catalog.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
|
|
||||||
// Setup/Run the controller manager
|
// Setup/Run the controller manager
|
||||||
mgr := controller.NewManager(client, testutil.Logger(t))
|
mgr := controller.NewManager(client, testutil.Logger(t))
|
||||||
|
|
|
@ -449,7 +449,10 @@ type controllerSuite struct {
|
||||||
func (suite *controllerSuite) SetupTest() {
|
func (suite *controllerSuite) SetupTest() {
|
||||||
suite.tenancies = resourcetest.TestTenancies()
|
suite.tenancies = resourcetest.TestTenancies()
|
||||||
suite.ctx = testutil.TestContext(suite.T())
|
suite.ctx = testutil.TestContext(suite.T())
|
||||||
client := svctest.RunResourceServiceWithTenancies(suite.T(), types.Register)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(types.Register).
|
||||||
|
WithTenancies(suite.tenancies...).
|
||||||
|
Run(suite.T())
|
||||||
suite.rt = controller.Runtime{
|
suite.rt = controller.Runtime{
|
||||||
Client: client,
|
Client: client,
|
||||||
Logger: testutil.Logger(suite.T()),
|
Logger: testutil.Logger(suite.T()),
|
||||||
|
|
|
@ -49,9 +49,11 @@ type reconciliationDataSuite struct {
|
||||||
|
|
||||||
func (suite *reconciliationDataSuite) SetupTest() {
|
func (suite *reconciliationDataSuite) SetupTest() {
|
||||||
suite.ctx = testutil.TestContext(suite.T())
|
suite.ctx = testutil.TestContext(suite.T())
|
||||||
|
|
||||||
suite.tenancies = rtest.TestTenancies()
|
suite.tenancies = rtest.TestTenancies()
|
||||||
resourceClient := svctest.RunResourceServiceWithTenancies(suite.T(), types.Register)
|
resourceClient := svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(types.Register).
|
||||||
|
WithTenancies(suite.tenancies...).
|
||||||
|
Run(suite.T())
|
||||||
suite.client = resourcetest.NewClient(resourceClient)
|
suite.client = resourcetest.NewClient(resourceClient)
|
||||||
suite.rt = controller.Runtime{
|
suite.rt = controller.Runtime{
|
||||||
Client: suite.client,
|
Client: suite.client,
|
||||||
|
|
|
@ -36,17 +36,19 @@ type controllerSuite struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *controllerSuite) SetupTest() {
|
func (suite *controllerSuite) SetupTest() {
|
||||||
suite.ctx = testutil.TestContext(suite.T())
|
suite.tenancies = rtest.TestTenancies()
|
||||||
client := svctest.RunResourceServiceWithTenancies(suite.T(), types.Register, types.RegisterDNSPolicy)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(types.Register, types.RegisterDNSPolicy).
|
||||||
|
WithTenancies(suite.tenancies...).
|
||||||
|
Run(suite.T())
|
||||||
|
|
||||||
suite.rt = controller.Runtime{
|
suite.rt = controller.Runtime{
|
||||||
Client: client,
|
Client: client,
|
||||||
Logger: testutil.Logger(suite.T()),
|
Logger: testutil.Logger(suite.T()),
|
||||||
}
|
}
|
||||||
suite.client = rtest.NewClient(client)
|
suite.client = rtest.NewClient(client)
|
||||||
|
|
||||||
suite.failoverMapper = failovermapper.New()
|
suite.failoverMapper = failovermapper.New()
|
||||||
|
suite.ctx = testutil.TestContext(suite.T())
|
||||||
suite.tenancies = rtest.TestTenancies()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *controllerSuite) TestController() {
|
func (suite *controllerSuite) TestController() {
|
||||||
|
|
|
@ -14,7 +14,6 @@ import (
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
mockres "github.com/hashicorp/consul/agent/grpc-external/services/resource"
|
|
||||||
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
|
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
|
||||||
"github.com/hashicorp/consul/internal/catalog/internal/types"
|
"github.com/hashicorp/consul/internal/catalog/internal/types"
|
||||||
"github.com/hashicorp/consul/internal/controller"
|
"github.com/hashicorp/consul/internal/controller"
|
||||||
|
@ -82,18 +81,12 @@ func (suite *nodeHealthControllerTestSuite) writeNode(name string, tenancy *pbre
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *nodeHealthControllerTestSuite) SetupTest() {
|
func (suite *nodeHealthControllerTestSuite) SetupTest() {
|
||||||
mockTenancyBridge := &mockres.MockTenancyBridge{}
|
|
||||||
suite.tenancies = resourcetest.TestTenancies()
|
suite.tenancies = resourcetest.TestTenancies()
|
||||||
for _, tenancy := range suite.tenancies {
|
client := svctest.NewResourceServiceBuilder().
|
||||||
mockTenancyBridge.On("PartitionExists", tenancy.Partition).Return(true, nil)
|
WithRegisterFns(types.Register, types.RegisterDNSPolicy).
|
||||||
mockTenancyBridge.On("NamespaceExists", tenancy.Partition, tenancy.Namespace).Return(true, nil)
|
WithTenancies(suite.tenancies...).
|
||||||
mockTenancyBridge.On("IsPartitionMarkedForDeletion", tenancy.Partition).Return(false, nil)
|
Run(suite.T())
|
||||||
mockTenancyBridge.On("IsNamespaceMarkedForDeletion", tenancy.Partition, tenancy.Namespace).Return(false, nil)
|
|
||||||
}
|
|
||||||
cfg := mockres.Config{
|
|
||||||
TenancyBridge: mockTenancyBridge,
|
|
||||||
}
|
|
||||||
client := svctest.RunResourceServiceWithConfig(suite.T(), cfg, types.Register, types.RegisterDNSPolicy)
|
|
||||||
suite.resourceClient = resourcetest.NewClient(client)
|
suite.resourceClient = resourcetest.NewClient(client)
|
||||||
suite.runtime = controller.Runtime{Client: suite.resourceClient, Logger: testutil.Logger(suite.T())}
|
suite.runtime = controller.Runtime{Client: suite.resourceClient, Logger: testutil.Logger(suite.T())}
|
||||||
suite.isEnterprise = versiontest.IsEnterprise()
|
suite.isEnterprise = versiontest.IsEnterprise()
|
||||||
|
|
|
@ -15,7 +15,6 @@ import (
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
"google.golang.org/protobuf/testing/protocmp"
|
"google.golang.org/protobuf/testing/protocmp"
|
||||||
|
|
||||||
svc "github.com/hashicorp/consul/agent/grpc-external/services/resource"
|
|
||||||
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
|
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
|
||||||
"github.com/hashicorp/consul/internal/catalog/internal/controllers/nodehealth"
|
"github.com/hashicorp/consul/internal/catalog/internal/controllers/nodehealth"
|
||||||
"github.com/hashicorp/consul/internal/catalog/internal/mappers/nodemapper"
|
"github.com/hashicorp/consul/internal/catalog/internal/mappers/nodemapper"
|
||||||
|
@ -93,15 +92,10 @@ type controllerSuite struct {
|
||||||
|
|
||||||
func (suite *controllerSuite) SetupTest() {
|
func (suite *controllerSuite) SetupTest() {
|
||||||
suite.tenancies = resourcetest.TestTenancies()
|
suite.tenancies = resourcetest.TestTenancies()
|
||||||
mockTenancyBridge := &svc.MockTenancyBridge{}
|
suite.client = svctest.NewResourceServiceBuilder().
|
||||||
for _, tenancy := range suite.tenancies {
|
WithRegisterFns(types.Register).
|
||||||
mockTenancyBridge.On("PartitionExists", tenancy.Partition).Return(true, nil)
|
WithTenancies(suite.tenancies...).
|
||||||
mockTenancyBridge.On("IsPartitionMarkedForDeletion", tenancy.Partition).Return(false, nil)
|
Run(suite.T())
|
||||||
mockTenancyBridge.On("NamespaceExists", tenancy.Partition, tenancy.Namespace).Return(true, nil)
|
|
||||||
mockTenancyBridge.On("IsNamespaceMarkedForDeletion", tenancy.Partition, tenancy.Namespace).Return(false, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
suite.client = svctest.RunResourceServiceWithConfig(suite.T(), svc.Config{TenancyBridge: mockTenancyBridge}, types.Register)
|
|
||||||
suite.runtime = controller.Runtime{Client: suite.client, Logger: testutil.Logger(suite.T())}
|
suite.runtime = controller.Runtime{Client: suite.client, Logger: testutil.Logger(suite.T())}
|
||||||
suite.isEnterprise = versiontest.IsEnterprise()
|
suite.isEnterprise = versiontest.IsEnterprise()
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,9 @@ func TestController_API(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
rec := newTestReconciler()
|
rec := newTestReconciler()
|
||||||
client := svctest.RunResourceService(t, demo.RegisterTypes)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
|
|
||||||
concertsChan := make(chan controller.Event)
|
concertsChan := make(chan controller.Event)
|
||||||
defer close(concertsChan)
|
defer close(concertsChan)
|
||||||
|
@ -164,7 +166,9 @@ func TestController_Placement(t *testing.T) {
|
||||||
|
|
||||||
t.Run("singleton", func(t *testing.T) {
|
t.Run("singleton", func(t *testing.T) {
|
||||||
rec := newTestReconciler()
|
rec := newTestReconciler()
|
||||||
client := svctest.RunResourceService(t, demo.RegisterTypes)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
|
|
||||||
ctrl := controller.
|
ctrl := controller.
|
||||||
ForType(demo.TypeV2Artist).
|
ForType(demo.TypeV2Artist).
|
||||||
|
@ -197,7 +201,9 @@ func TestController_Placement(t *testing.T) {
|
||||||
|
|
||||||
t.Run("each server", func(t *testing.T) {
|
t.Run("each server", func(t *testing.T) {
|
||||||
rec := newTestReconciler()
|
rec := newTestReconciler()
|
||||||
client := svctest.RunResourceService(t, demo.RegisterTypes)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
|
|
||||||
ctrl := controller.
|
ctrl := controller.
|
||||||
ForType(demo.TypeV2Artist).
|
ForType(demo.TypeV2Artist).
|
||||||
|
@ -233,7 +239,10 @@ func TestController_String(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestController_NoReconciler(t *testing.T) {
|
func TestController_NoReconciler(t *testing.T) {
|
||||||
client := svctest.RunResourceService(t, demo.RegisterTypes)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
|
|
||||||
mgr := controller.NewManager(client, testutil.Logger(t))
|
mgr := controller.NewManager(client, testutil.Logger(t))
|
||||||
|
|
||||||
ctrl := controller.ForType(demo.TypeV2Artist)
|
ctrl := controller.ForType(demo.TypeV2Artist)
|
||||||
|
@ -248,7 +257,9 @@ func TestController_Watch(t *testing.T) {
|
||||||
t.Run("partitioned scoped resources", func(t *testing.T) {
|
t.Run("partitioned scoped resources", func(t *testing.T) {
|
||||||
rec := newTestReconciler()
|
rec := newTestReconciler()
|
||||||
|
|
||||||
client := svctest.RunResourceService(t, demo.RegisterTypes)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
|
|
||||||
ctrl := controller.
|
ctrl := controller.
|
||||||
ForType(demo.TypeV1RecordLabel).
|
ForType(demo.TypeV1RecordLabel).
|
||||||
|
@ -274,7 +285,9 @@ func TestController_Watch(t *testing.T) {
|
||||||
t.Run("cluster scoped resources", func(t *testing.T) {
|
t.Run("cluster scoped resources", func(t *testing.T) {
|
||||||
rec := newTestReconciler()
|
rec := newTestReconciler()
|
||||||
|
|
||||||
client := svctest.RunResourceService(t, demo.RegisterTypes)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
|
|
||||||
ctrl := controller.
|
ctrl := controller.
|
||||||
ForType(demo.TypeV1Executive).
|
ForType(demo.TypeV1Executive).
|
||||||
|
@ -299,7 +312,9 @@ func TestController_Watch(t *testing.T) {
|
||||||
t.Run("namespace scoped resources", func(t *testing.T) {
|
t.Run("namespace scoped resources", func(t *testing.T) {
|
||||||
rec := newTestReconciler()
|
rec := newTestReconciler()
|
||||||
|
|
||||||
client := svctest.RunResourceService(t, demo.RegisterTypes)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
|
|
||||||
ctrl := controller.
|
ctrl := controller.
|
||||||
ForType(demo.TypeV2Artist).
|
ForType(demo.TypeV2Artist).
|
||||||
|
|
|
@ -192,7 +192,10 @@ func TestFindDuplicates(t *testing.T) {
|
||||||
|
|
||||||
func (suite *controllerTestSuite) SetupTest() {
|
func (suite *controllerTestSuite) SetupTest() {
|
||||||
suite.tenancies = resourcetest.TestTenancies()
|
suite.tenancies = resourcetest.TestTenancies()
|
||||||
resourceClient := svctest.RunResourceServiceWithTenancies(suite.T(), types.Register, catalog.RegisterTypes)
|
resourceClient := svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(types.Register, catalog.RegisterTypes).
|
||||||
|
WithTenancies(suite.tenancies...).
|
||||||
|
Run(suite.T())
|
||||||
suite.runtime = controller.Runtime{Client: resourceClient, Logger: testutil.Logger(suite.T())}
|
suite.runtime = controller.Runtime{Client: resourceClient, Logger: testutil.Logger(suite.T())}
|
||||||
suite.ctx = testutil.TestContext(suite.T())
|
suite.ctx = testutil.TestContext(suite.T())
|
||||||
suite.client = resourcetest.NewClient(resourceClient)
|
suite.client = resourcetest.NewClient(resourceClient)
|
||||||
|
|
|
@ -53,7 +53,10 @@ type controllerTestSuite struct {
|
||||||
|
|
||||||
func (suite *controllerTestSuite) SetupTest() {
|
func (suite *controllerTestSuite) SetupTest() {
|
||||||
suite.tenancies = resourcetest.TestTenancies()
|
suite.tenancies = resourcetest.TestTenancies()
|
||||||
resourceClient := svctest.RunResourceServiceWithTenancies(suite.T(), types.Register, catalog.RegisterTypes)
|
resourceClient := svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(types.Register, catalog.RegisterTypes).
|
||||||
|
WithTenancies(suite.tenancies...).
|
||||||
|
Run(suite.T())
|
||||||
suite.client = resourcetest.NewClient(resourceClient)
|
suite.client = resourcetest.NewClient(resourceClient)
|
||||||
suite.runtime = controller.Runtime{Client: resourceClient, Logger: testutil.Logger(suite.T())}
|
suite.runtime = controller.Runtime{Client: resourceClient, Logger: testutil.Logger(suite.T())}
|
||||||
suite.ctx = testutil.TestContext(suite.T())
|
suite.ctx = testutil.TestContext(suite.T())
|
||||||
|
|
|
@ -94,7 +94,9 @@ func TestSortProxyConfigurations(t *testing.T) {
|
||||||
|
|
||||||
for name, c := range cases {
|
for name, c := range cases {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
resourceClient := svctest.RunResourceService(t, types.Register)
|
resourceClient := svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(types.Register).
|
||||||
|
Run(t)
|
||||||
|
|
||||||
var decProxyCfgs []*types.DecodedProxyConfiguration
|
var decProxyCfgs []*types.DecodedProxyConfiguration
|
||||||
for i, ws := range c.selectors {
|
for i, ws := range c.selectors {
|
||||||
|
|
|
@ -37,7 +37,11 @@ type controllerSuite struct {
|
||||||
func (suite *controllerSuite) SetupTest() {
|
func (suite *controllerSuite) SetupTest() {
|
||||||
suite.ctx = testutil.TestContext(suite.T())
|
suite.ctx = testutil.TestContext(suite.T())
|
||||||
suite.tenancies = rtest.TestTenancies()
|
suite.tenancies = rtest.TestTenancies()
|
||||||
client := svctest.RunResourceServiceWithTenancies(suite.T(), types.Register, catalog.RegisterTypes)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(types.Register, catalog.RegisterTypes).
|
||||||
|
WithTenancies(suite.tenancies...).
|
||||||
|
Run(suite.T())
|
||||||
|
|
||||||
suite.rt = controller.Runtime{
|
suite.rt = controller.Runtime{
|
||||||
Client: client,
|
Client: client,
|
||||||
Logger: testutil.Logger(suite.T()),
|
Logger: testutil.Logger(suite.T()),
|
||||||
|
|
|
@ -7,11 +7,10 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/go-hclog"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"google.golang.org/protobuf/types/known/durationpb"
|
"google.golang.org/protobuf/types/known/durationpb"
|
||||||
|
|
||||||
pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v2beta1"
|
"github.com/hashicorp/go-hclog"
|
||||||
|
|
||||||
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
|
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
|
||||||
"github.com/hashicorp/consul/internal/catalog"
|
"github.com/hashicorp/consul/internal/catalog"
|
||||||
|
@ -21,6 +20,7 @@ import (
|
||||||
"github.com/hashicorp/consul/internal/resource"
|
"github.com/hashicorp/consul/internal/resource"
|
||||||
rtest "github.com/hashicorp/consul/internal/resource/resourcetest"
|
rtest "github.com/hashicorp/consul/internal/resource/resourcetest"
|
||||||
pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v2beta1"
|
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"
|
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||||
"github.com/hashicorp/consul/proto/private/prototest"
|
"github.com/hashicorp/consul/proto/private/prototest"
|
||||||
"github.com/hashicorp/consul/sdk/testutil"
|
"github.com/hashicorp/consul/sdk/testutil"
|
||||||
|
@ -28,7 +28,9 @@ import (
|
||||||
|
|
||||||
func TestLoadResourcesForComputedRoutes(t *testing.T) {
|
func TestLoadResourcesForComputedRoutes(t *testing.T) {
|
||||||
ctx := testutil.TestContext(t)
|
ctx := testutil.TestContext(t)
|
||||||
rclient := svctest.RunResourceService(t, types.Register, catalog.RegisterTypes)
|
rclient := svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(types.Register, catalog.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
rt := controller.Runtime{
|
rt := controller.Runtime{
|
||||||
Client: rclient,
|
Client: rclient,
|
||||||
Logger: testutil.Logger(t),
|
Logger: testutil.Logger(t),
|
||||||
|
|
|
@ -5,7 +5,6 @@ package cache
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/hashicorp/consul/internal/mesh/internal/controllers/routes/routestest"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -13,6 +12,7 @@ import (
|
||||||
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
|
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
|
||||||
"github.com/hashicorp/consul/internal/catalog"
|
"github.com/hashicorp/consul/internal/catalog"
|
||||||
"github.com/hashicorp/consul/internal/controller"
|
"github.com/hashicorp/consul/internal/controller"
|
||||||
|
"github.com/hashicorp/consul/internal/mesh/internal/controllers/routes/routestest"
|
||||||
"github.com/hashicorp/consul/internal/mesh/internal/types"
|
"github.com/hashicorp/consul/internal/mesh/internal/types"
|
||||||
"github.com/hashicorp/consul/internal/resource"
|
"github.com/hashicorp/consul/internal/resource"
|
||||||
"github.com/hashicorp/consul/internal/resource/resourcetest"
|
"github.com/hashicorp/consul/internal/resource/resourcetest"
|
||||||
|
@ -82,7 +82,10 @@ func TestIdentities(t *testing.T) {
|
||||||
|
|
||||||
func TestMapComputedTrafficPermissions(t *testing.T) {
|
func TestMapComputedTrafficPermissions(t *testing.T) {
|
||||||
resourcetest.RunWithTenancies(func(tenancy *pbresource.Tenancy) {
|
resourcetest.RunWithTenancies(func(tenancy *pbresource.Tenancy) {
|
||||||
client := svctest.RunResourceService(t, types.Register, catalog.RegisterTypes)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(types.Register, catalog.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
|
|
||||||
ctp := resourcetest.Resource(pbauth.ComputedTrafficPermissionsType, "workload-identity-1").
|
ctp := resourcetest.Resource(pbauth.ComputedTrafficPermissionsType, "workload-identity-1").
|
||||||
WithTenancy(tenancy).
|
WithTenancy(tenancy).
|
||||||
WithData(t, &pbauth.ComputedTrafficPermissions{}).
|
WithData(t, &pbauth.ComputedTrafficPermissions{}).
|
||||||
|
@ -137,7 +140,9 @@ func TestUnified_AllMappingsToProxyStateTemplate(t *testing.T) {
|
||||||
resourcetest.RunWithTenancies(func(tenancy *pbresource.Tenancy) {
|
resourcetest.RunWithTenancies(func(tenancy *pbresource.Tenancy) {
|
||||||
var (
|
var (
|
||||||
cache = New()
|
cache = New()
|
||||||
client = svctest.RunResourceService(t, types.Register, catalog.RegisterTypes)
|
client = svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(types.Register, catalog.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
)
|
)
|
||||||
|
|
||||||
anyServiceData := &pbcatalog.Service{
|
anyServiceData := &pbcatalog.Service{
|
||||||
|
|
|
@ -12,7 +12,6 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
mockres "github.com/hashicorp/consul/agent/grpc-external/services/resource"
|
|
||||||
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
|
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
|
||||||
"github.com/hashicorp/consul/envoyextensions/xdscommon"
|
"github.com/hashicorp/consul/envoyextensions/xdscommon"
|
||||||
"github.com/hashicorp/consul/internal/auth"
|
"github.com/hashicorp/consul/internal/auth"
|
||||||
|
@ -85,17 +84,11 @@ type apiData struct {
|
||||||
|
|
||||||
func (suite *controllerTestSuite) SetupTest() {
|
func (suite *controllerTestSuite) SetupTest() {
|
||||||
suite.tenancies = resourcetest.TestTenancies()
|
suite.tenancies = resourcetest.TestTenancies()
|
||||||
mockTenancyBridge := &mockres.MockTenancyBridge{}
|
resourceClient := svctest.NewResourceServiceBuilder().
|
||||||
for _, tenancy := range suite.tenancies {
|
WithRegisterFns(types.Register, catalog.RegisterTypes, auth.RegisterTypes).
|
||||||
mockTenancyBridge.On("PartitionExists", tenancy.Partition).Return(true, nil)
|
WithTenancies(suite.tenancies...).
|
||||||
mockTenancyBridge.On("NamespaceExists", tenancy.Partition, tenancy.Namespace).Return(true, nil)
|
Run(suite.T())
|
||||||
mockTenancyBridge.On("IsPartitionMarkedForDeletion", tenancy.Partition).Return(false, nil)
|
|
||||||
mockTenancyBridge.On("IsNamespaceMarkedForDeletion", tenancy.Partition, tenancy.Namespace).Return(false, nil)
|
|
||||||
}
|
|
||||||
cfg := mockres.Config{
|
|
||||||
TenancyBridge: mockTenancyBridge,
|
|
||||||
}
|
|
||||||
resourceClient := svctest.RunResourceServiceWithConfig(suite.T(), cfg, types.Register, catalog.RegisterTypes, auth.RegisterTypes)
|
|
||||||
suite.client = resourcetest.NewClient(resourceClient)
|
suite.client = resourcetest.NewClient(resourceClient)
|
||||||
suite.runtime = controller.Runtime{Client: resourceClient, Logger: testutil.Logger(suite.T())}
|
suite.runtime = controller.Runtime{Client: resourceClient, Logger: testutil.Logger(suite.T())}
|
||||||
suite.ctx = testutil.TestContext(suite.T())
|
suite.ctx = testutil.TestContext(suite.T())
|
||||||
|
|
|
@ -53,8 +53,10 @@ type dataFetcherSuite struct {
|
||||||
func (suite *dataFetcherSuite) SetupTest() {
|
func (suite *dataFetcherSuite) SetupTest() {
|
||||||
suite.ctx = testutil.TestContext(suite.T())
|
suite.ctx = testutil.TestContext(suite.T())
|
||||||
suite.tenancies = resourcetest.TestTenancies()
|
suite.tenancies = resourcetest.TestTenancies()
|
||||||
suite.tenancies = resourcetest.TestTenancies()
|
suite.client = svctest.NewResourceServiceBuilder().
|
||||||
suite.client = svctest.RunResourceServiceWithTenancies(suite.T(), types.Register, catalog.RegisterTypes)
|
WithRegisterFns(types.Register, catalog.RegisterTypes).
|
||||||
|
WithTenancies(suite.tenancies...).
|
||||||
|
Run(suite.T())
|
||||||
suite.resourceClient = resourcetest.NewClient(suite.client)
|
suite.resourceClient = resourcetest.NewClient(suite.client)
|
||||||
suite.rt = controller.Runtime{
|
suite.rt = controller.Runtime{
|
||||||
Client: suite.client,
|
Client: suite.client,
|
||||||
|
|
|
@ -70,7 +70,12 @@ type xdsControllerTestSuite struct {
|
||||||
|
|
||||||
func (suite *xdsControllerTestSuite) SetupTest() {
|
func (suite *xdsControllerTestSuite) SetupTest() {
|
||||||
suite.ctx = testutil.TestContext(suite.T())
|
suite.ctx = testutil.TestContext(suite.T())
|
||||||
resourceClient := svctest.RunResourceServiceWithTenancies(suite.T(), types.Register, catalog.RegisterTypes)
|
suite.tenancies = resourcetest.TestTenancies()
|
||||||
|
resourceClient := svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(types.Register, catalog.RegisterTypes).
|
||||||
|
WithTenancies(suite.tenancies...).
|
||||||
|
Run(suite.T())
|
||||||
|
|
||||||
suite.runtime = controller.Runtime{Client: resourceClient, Logger: testutil.Logger(suite.T())}
|
suite.runtime = controller.Runtime{Client: resourceClient, Logger: testutil.Logger(suite.T())}
|
||||||
suite.client = resourcetest.NewClient(resourceClient)
|
suite.client = resourcetest.NewClient(resourceClient)
|
||||||
suite.fetcher = mockFetcher
|
suite.fetcher = mockFetcher
|
||||||
|
@ -100,8 +105,6 @@ func (suite *xdsControllerTestSuite) SetupTest() {
|
||||||
leafCertEvents: suite.leafCertEvents,
|
leafCertEvents: suite.leafCertEvents,
|
||||||
datacenter: "dc1",
|
datacenter: "dc1",
|
||||||
}
|
}
|
||||||
|
|
||||||
suite.tenancies = resourcetest.TestTenancies()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func mockFetcher() (*pbproxystate.TrustBundle, error) {
|
func mockFetcher() (*pbproxystate.TrustBundle, error) {
|
||||||
|
|
|
@ -21,7 +21,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMapSelector(t *testing.T) {
|
func TestMapSelector(t *testing.T) {
|
||||||
client := svctest.RunResourceService(t, types.Register, catalog.RegisterTypes)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(types.Register, catalog.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
|
|
||||||
// Create some workloads.
|
// Create some workloads.
|
||||||
// For this test, we don't care about the workload data, so we will re-use
|
// For this test, we don't care about the workload data, so we will re-use
|
||||||
|
|
|
@ -22,7 +22,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMapToComputedType(t *testing.T) {
|
func TestMapToComputedType(t *testing.T) {
|
||||||
resourceClient := svctest.RunResourceService(t, types.Register, catalog.RegisterTypes)
|
resourceClient := svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(types.Register, catalog.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
|
|
||||||
mapper := New[*pbmesh.ProxyConfiguration](pbmesh.ComputedProxyConfigurationType)
|
mapper := New[*pbmesh.ProxyConfiguration](pbmesh.ComputedProxyConfigurationType)
|
||||||
|
|
||||||
workloadData := &pbcatalog.Workload{
|
workloadData := &pbcatalog.Workload{
|
||||||
|
|
|
@ -11,9 +11,8 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
svc "github.com/hashicorp/consul/agent/grpc-external/services/resource"
|
|
||||||
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
|
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
|
||||||
cat "github.com/hashicorp/consul/internal/catalog"
|
"github.com/hashicorp/consul/internal/catalog"
|
||||||
"github.com/hashicorp/consul/internal/controller"
|
"github.com/hashicorp/consul/internal/controller"
|
||||||
"github.com/hashicorp/consul/internal/multicluster/internal/types"
|
"github.com/hashicorp/consul/internal/multicluster/internal/types"
|
||||||
"github.com/hashicorp/consul/internal/resource"
|
"github.com/hashicorp/consul/internal/resource"
|
||||||
|
@ -39,20 +38,12 @@ type controllerSuite struct {
|
||||||
func (suite *controllerSuite) SetupTest() {
|
func (suite *controllerSuite) SetupTest() {
|
||||||
suite.ctx = testutil.TestContext(suite.T())
|
suite.ctx = testutil.TestContext(suite.T())
|
||||||
suite.tenancies = rtest.TestTenancies()
|
suite.tenancies = rtest.TestTenancies()
|
||||||
mockTenancyBridge := &svc.MockTenancyBridge{}
|
client := svctest.NewResourceServiceBuilder().
|
||||||
for _, tenancy := range suite.tenancies {
|
WithRegisterFns(types.Register, catalog.RegisterTypes).
|
||||||
mockTenancyBridge.On("PartitionExists", tenancy.Partition).Return(true, nil)
|
WithTenancies(suite.tenancies...).
|
||||||
mockTenancyBridge.On("IsPartitionMarkedForDeletion", tenancy.Partition).Return(false, nil)
|
WithTenancies(rtest.Tenancy("default.app"), rtest.Tenancy("foo.app")).
|
||||||
mockTenancyBridge.On("NamespaceExists", tenancy.Partition, tenancy.Namespace).Return(true, nil)
|
Run(suite.T())
|
||||||
mockTenancyBridge.On("IsNamespaceMarkedForDeletion", tenancy.Partition, tenancy.Namespace).Return(false, nil)
|
|
||||||
mockTenancyBridge.On("NamespaceExists", tenancy.Partition, "app").Return(true, nil)
|
|
||||||
mockTenancyBridge.On("IsNamespaceMarkedForDeletion", tenancy.Partition, "app").Return(false, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
config := svc.Config{
|
|
||||||
TenancyBridge: mockTenancyBridge,
|
|
||||||
}
|
|
||||||
client := svctest.RunResourceServiceWithConfig(suite.T(), config, types.Register, cat.RegisterTypes)
|
|
||||||
suite.client = rtest.NewClient(client)
|
suite.client = rtest.NewClient(client)
|
||||||
suite.rt = controller.Runtime{
|
suite.rt = controller.Runtime{
|
||||||
Client: suite.client,
|
Client: suite.client,
|
||||||
|
|
|
@ -22,7 +22,7 @@ import (
|
||||||
|
|
||||||
func TestGetDecodedResource(t *testing.T) {
|
func TestGetDecodedResource(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
baseClient = svctest.RunResourceService(t, demo.RegisterTypes)
|
baseClient = svctest.NewResourceServiceBuilder().WithRegisterFns(demo.RegisterTypes).Run(t)
|
||||||
client = rtest.NewClient(baseClient)
|
client = rtest.NewClient(baseClient)
|
||||||
ctx = testutil.TestContext(t)
|
ctx = testutil.TestContext(t)
|
||||||
)
|
)
|
||||||
|
|
|
@ -16,7 +16,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestArtistReconciler(t *testing.T) {
|
func TestArtistReconciler(t *testing.T) {
|
||||||
client := svctest.RunResourceService(t, RegisterTypes)
|
client := svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(RegisterTypes).
|
||||||
|
Run(t)
|
||||||
|
|
||||||
// Seed the database with an artist.
|
// Seed the database with an artist.
|
||||||
res, err := GenerateV2Artist()
|
res, err := GenerateV2Artist()
|
||||||
|
|
|
@ -12,17 +12,17 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hashicorp/go-hclog"
|
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
resourceSvc "github.com/hashicorp/consul/agent/grpc-external/services/resource"
|
"github.com/hashicorp/go-hclog"
|
||||||
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
|
|
||||||
pbdemov1 "github.com/hashicorp/consul/proto/private/pbdemo/v1"
|
|
||||||
|
|
||||||
|
svc "github.com/hashicorp/consul/agent/grpc-external/services/resource"
|
||||||
|
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
|
||||||
"github.com/hashicorp/consul/internal/resource"
|
"github.com/hashicorp/consul/internal/resource"
|
||||||
"github.com/hashicorp/consul/internal/resource/demo"
|
"github.com/hashicorp/consul/internal/resource/demo"
|
||||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
"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"
|
pbdemov2 "github.com/hashicorp/consul/proto/private/pbdemo/v2"
|
||||||
"github.com/hashicorp/consul/sdk/testutil"
|
"github.com/hashicorp/consul/sdk/testutil"
|
||||||
)
|
)
|
||||||
|
@ -44,7 +44,11 @@ func TestResourceHandler_InputValidation(t *testing.T) {
|
||||||
expectedResponseCode int
|
expectedResponseCode int
|
||||||
responseBodyContains string
|
responseBodyContains string
|
||||||
}
|
}
|
||||||
client := svctest.RunResourceService(t, demo.RegisterTypes)
|
|
||||||
|
client := svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
|
|
||||||
resourceHandler := resourceHandler{
|
resourceHandler := resourceHandler{
|
||||||
resource.Registration{
|
resource.Registration{
|
||||||
Type: demo.TypeV2Artist,
|
Type: demo.TypeV2Artist,
|
||||||
|
@ -125,17 +129,17 @@ func TestResourceHandler_InputValidation(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResourceWriteHandler(t *testing.T) {
|
func TestResourceWriteHandler(t *testing.T) {
|
||||||
aclResolver := &resourceSvc.MockACLResolver{}
|
aclResolver := &svc.MockACLResolver{}
|
||||||
aclResolver.On("ResolveTokenAndDefaultMeta", testACLTokenArtistReadPolicy, mock.Anything, mock.Anything).
|
aclResolver.On("ResolveTokenAndDefaultMeta", testACLTokenArtistReadPolicy, mock.Anything, mock.Anything).
|
||||||
Return(svctest.AuthorizerFrom(t, demo.ArtistV1ReadPolicy, demo.ArtistV2ReadPolicy), nil)
|
Return(svctest.AuthorizerFrom(t, demo.ArtistV1ReadPolicy, demo.ArtistV2ReadPolicy), nil)
|
||||||
aclResolver.On("ResolveTokenAndDefaultMeta", testACLTokenArtistWritePolicy, mock.Anything, mock.Anything).
|
aclResolver.On("ResolveTokenAndDefaultMeta", testACLTokenArtistWritePolicy, mock.Anything, mock.Anything).
|
||||||
Return(svctest.AuthorizerFrom(t, demo.ArtistV1WritePolicy, demo.ArtistV2WritePolicy), nil)
|
Return(svctest.AuthorizerFrom(t, demo.ArtistV1WritePolicy, demo.ArtistV2WritePolicy), nil)
|
||||||
|
|
||||||
client := svctest.RunResourceServiceWithConfig(t, resourceSvc.Config{ACLResolver: aclResolver}, demo.RegisterTypes)
|
builder := svctest.NewResourceServiceBuilder().
|
||||||
|
WithACLResolver(aclResolver).
|
||||||
r := resource.NewRegistry()
|
WithRegisterFns(demo.RegisterTypes)
|
||||||
demo.RegisterTypes(r)
|
client := builder.Run(t)
|
||||||
handler := NewHandler(client, r, parseToken, hclog.NewNullLogger())
|
handler := NewHandler(client, builder.Registry(), parseToken, hclog.NewNullLogger())
|
||||||
|
|
||||||
t.Run("should be blocked if the token is not authorized", func(t *testing.T) {
|
t.Run("should be blocked if the token is not authorized", func(t *testing.T) {
|
||||||
rsp := httptest.NewRecorder()
|
rsp := httptest.NewRecorder()
|
||||||
|
@ -157,6 +161,7 @@ func TestResourceWriteHandler(t *testing.T) {
|
||||||
|
|
||||||
require.Equal(t, http.StatusForbidden, rsp.Result().StatusCode)
|
require.Equal(t, http.StatusForbidden, rsp.Result().StatusCode)
|
||||||
})
|
})
|
||||||
|
|
||||||
var readRsp *pbresource.ReadResponse
|
var readRsp *pbresource.ReadResponse
|
||||||
t.Run("should write to the resource backend", func(t *testing.T) {
|
t.Run("should write to the resource backend", func(t *testing.T) {
|
||||||
rsp := httptest.NewRecorder()
|
rsp := httptest.NewRecorder()
|
||||||
|
@ -352,7 +357,7 @@ func deleteResource(t *testing.T, artistHandler http.Handler, resourceUri *Resou
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResourceReadHandler(t *testing.T) {
|
func TestResourceReadHandler(t *testing.T) {
|
||||||
aclResolver := &resourceSvc.MockACLResolver{}
|
aclResolver := &svc.MockACLResolver{}
|
||||||
aclResolver.On("ResolveTokenAndDefaultMeta", testACLTokenArtistReadPolicy, mock.Anything, mock.Anything).
|
aclResolver.On("ResolveTokenAndDefaultMeta", testACLTokenArtistReadPolicy, mock.Anything, mock.Anything).
|
||||||
Return(svctest.AuthorizerFrom(t, demo.ArtistV1ReadPolicy, demo.ArtistV2ReadPolicy), nil)
|
Return(svctest.AuthorizerFrom(t, demo.ArtistV1ReadPolicy, demo.ArtistV2ReadPolicy), nil)
|
||||||
aclResolver.On("ResolveTokenAndDefaultMeta", testACLTokenArtistWritePolicy, mock.Anything, mock.Anything).
|
aclResolver.On("ResolveTokenAndDefaultMeta", testACLTokenArtistWritePolicy, mock.Anything, mock.Anything).
|
||||||
|
@ -360,11 +365,11 @@ func TestResourceReadHandler(t *testing.T) {
|
||||||
aclResolver.On("ResolveTokenAndDefaultMeta", fakeToken, mock.Anything, mock.Anything).
|
aclResolver.On("ResolveTokenAndDefaultMeta", fakeToken, mock.Anything, mock.Anything).
|
||||||
Return(svctest.AuthorizerFrom(t, ""), nil)
|
Return(svctest.AuthorizerFrom(t, ""), nil)
|
||||||
|
|
||||||
client := svctest.RunResourceServiceWithConfig(t, resourceSvc.Config{ACLResolver: aclResolver}, demo.RegisterTypes)
|
builder := svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
r := resource.NewRegistry()
|
WithACLResolver(aclResolver)
|
||||||
demo.RegisterTypes(r)
|
client := builder.Run(t)
|
||||||
handler := NewHandler(client, r, parseToken, hclog.NewNullLogger())
|
handler := NewHandler(client, builder.Registry(), parseToken, hclog.NewNullLogger())
|
||||||
|
|
||||||
createdResource := createResource(t, handler, nil)
|
createdResource := createResource(t, handler, nil)
|
||||||
|
|
||||||
|
@ -407,18 +412,17 @@ func TestResourceReadHandler(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResourceDeleteHandler(t *testing.T) {
|
func TestResourceDeleteHandler(t *testing.T) {
|
||||||
aclResolver := &resourceSvc.MockACLResolver{}
|
aclResolver := &svc.MockACLResolver{}
|
||||||
aclResolver.On("ResolveTokenAndDefaultMeta", testACLTokenArtistReadPolicy, mock.Anything, mock.Anything).
|
aclResolver.On("ResolveTokenAndDefaultMeta", testACLTokenArtistReadPolicy, mock.Anything, mock.Anything).
|
||||||
Return(svctest.AuthorizerFrom(t, demo.ArtistV2ReadPolicy), nil)
|
Return(svctest.AuthorizerFrom(t, demo.ArtistV2ReadPolicy), nil)
|
||||||
aclResolver.On("ResolveTokenAndDefaultMeta", testACLTokenArtistWritePolicy, mock.Anything, mock.Anything).
|
aclResolver.On("ResolveTokenAndDefaultMeta", testACLTokenArtistWritePolicy, mock.Anything, mock.Anything).
|
||||||
Return(svctest.AuthorizerFrom(t, demo.ArtistV2WritePolicy), nil)
|
Return(svctest.AuthorizerFrom(t, demo.ArtistV2WritePolicy), nil)
|
||||||
|
|
||||||
client := svctest.RunResourceServiceWithConfig(t, resourceSvc.Config{ACLResolver: aclResolver}, demo.RegisterTypes)
|
builder := svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
r := resource.NewRegistry()
|
WithACLResolver(aclResolver)
|
||||||
demo.RegisterTypes(r)
|
client := builder.Run(t)
|
||||||
|
handler := NewHandler(client, builder.Registry(), parseToken, hclog.NewNullLogger())
|
||||||
handler := NewHandler(client, r, parseToken, hclog.NewNullLogger())
|
|
||||||
|
|
||||||
t.Run("should surface PermissionDenied error from resource service", func(t *testing.T) {
|
t.Run("should surface PermissionDenied error from resource service", func(t *testing.T) {
|
||||||
createResource(t, handler, nil)
|
createResource(t, handler, nil)
|
||||||
|
@ -484,18 +488,17 @@ func TestResourceDeleteHandler(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResourceListHandler(t *testing.T) {
|
func TestResourceListHandler(t *testing.T) {
|
||||||
aclResolver := &resourceSvc.MockACLResolver{}
|
aclResolver := &svc.MockACLResolver{}
|
||||||
aclResolver.On("ResolveTokenAndDefaultMeta", testACLTokenArtistListPolicy, mock.Anything, mock.Anything).
|
aclResolver.On("ResolveTokenAndDefaultMeta", testACLTokenArtistListPolicy, mock.Anything, mock.Anything).
|
||||||
Return(svctest.AuthorizerFrom(t, demo.ArtistV2ListPolicy), nil)
|
Return(svctest.AuthorizerFrom(t, demo.ArtistV2ListPolicy), nil)
|
||||||
aclResolver.On("ResolveTokenAndDefaultMeta", testACLTokenArtistWritePolicy, mock.Anything, mock.Anything).
|
aclResolver.On("ResolveTokenAndDefaultMeta", testACLTokenArtistWritePolicy, mock.Anything, mock.Anything).
|
||||||
Return(svctest.AuthorizerFrom(t, demo.ArtistV2WritePolicy), nil)
|
Return(svctest.AuthorizerFrom(t, demo.ArtistV2WritePolicy), nil)
|
||||||
|
|
||||||
client := svctest.RunResourceServiceWithConfig(t, resourceSvc.Config{ACLResolver: aclResolver}, demo.RegisterTypes)
|
builder := svctest.NewResourceServiceBuilder().
|
||||||
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
r := resource.NewRegistry()
|
WithACLResolver(aclResolver)
|
||||||
demo.RegisterTypes(r)
|
client := builder.Run(t)
|
||||||
|
handler := NewHandler(client, builder.Registry(), parseToken, hclog.NewNullLogger())
|
||||||
handler := NewHandler(client, r, parseToken, hclog.NewNullLogger())
|
|
||||||
|
|
||||||
t.Run("should return MethodNotAllowed", func(t *testing.T) {
|
t.Run("should return MethodNotAllowed", func(t *testing.T) {
|
||||||
rsp := httptest.NewRecorder()
|
rsp := httptest.NewRecorder()
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
package reaper
|
package reaper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/hashicorp/consul/internal/resource/resourcetest"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -16,13 +15,14 @@ import (
|
||||||
"github.com/hashicorp/consul/internal/controller"
|
"github.com/hashicorp/consul/internal/controller"
|
||||||
"github.com/hashicorp/consul/internal/resource"
|
"github.com/hashicorp/consul/internal/resource"
|
||||||
"github.com/hashicorp/consul/internal/resource/demo"
|
"github.com/hashicorp/consul/internal/resource/demo"
|
||||||
|
"github.com/hashicorp/consul/internal/resource/resourcetest"
|
||||||
|
rtest "github.com/hashicorp/consul/internal/resource/resourcetest"
|
||||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||||
"github.com/hashicorp/consul/sdk/testutil"
|
"github.com/hashicorp/consul/sdk/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestReconcile_ResourceWithNoChildren(t *testing.T) {
|
func TestReconcile_ResourceWithNoChildren(t *testing.T) {
|
||||||
client := svctest.RunResourceServiceWithTenancies(t, demo.RegisterTypes)
|
client := setupResourceService(t)
|
||||||
|
|
||||||
runReaperTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
|
runReaperTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
|
||||||
// Seed the database with an artist.
|
// Seed the database with an artist.
|
||||||
res, err := demo.GenerateV2Artist()
|
res, err := demo.GenerateV2Artist()
|
||||||
|
@ -79,8 +79,7 @@ func TestReconcile_ResourceWithNoChildren(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReconcile_ResourceWithChildren(t *testing.T) {
|
func TestReconcile_ResourceWithChildren(t *testing.T) {
|
||||||
client := svctest.RunResourceServiceWithTenancies(t, demo.RegisterTypes)
|
client := setupResourceService(t)
|
||||||
|
|
||||||
runReaperTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
|
runReaperTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
|
||||||
// Seed the database with an artist
|
// Seed the database with an artist
|
||||||
res, err := demo.GenerateV2Artist()
|
res, err := demo.GenerateV2Artist()
|
||||||
|
@ -162,8 +161,7 @@ func TestReconcile_ResourceWithChildren(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReconcile_RequeueWithDelayWhenSecondPassDelayNotElapsed(t *testing.T) {
|
func TestReconcile_RequeueWithDelayWhenSecondPassDelayNotElapsed(t *testing.T) {
|
||||||
client := svctest.RunResourceServiceWithTenancies(t, demo.RegisterTypes)
|
client := setupResourceService(t)
|
||||||
|
|
||||||
runReaperTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
|
runReaperTestCaseWithTenancies(func(tenancy *pbresource.Tenancy) {
|
||||||
// Seed the database with an artist.
|
// Seed the database with an artist.
|
||||||
res, err := demo.GenerateV2Artist()
|
res, err := demo.GenerateV2Artist()
|
||||||
|
@ -216,3 +214,10 @@ func runReaperTestCaseWithTenancies(testCase func(tenancy *pbresource.Tenancy))
|
||||||
testCase(tenancy)
|
testCase(tenancy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setupResourceService(t *testing.T) pbresource.ResourceServiceClient {
|
||||||
|
return svctest.NewResourceServiceBuilder().
|
||||||
|
WithTenancies(rtest.TestTenancies()...).
|
||||||
|
WithRegisterFns(demo.RegisterTypes).
|
||||||
|
Run(t)
|
||||||
|
}
|
||||||
|
|
|
@ -10,13 +10,11 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
svc "github.com/hashicorp/consul/agent/grpc-external/services/resource"
|
|
||||||
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
|
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
|
||||||
"github.com/hashicorp/consul/internal/controller"
|
"github.com/hashicorp/consul/internal/controller"
|
||||||
"github.com/hashicorp/consul/internal/resource"
|
"github.com/hashicorp/consul/internal/resource"
|
||||||
"github.com/hashicorp/consul/internal/resource/demo"
|
"github.com/hashicorp/consul/internal/resource/demo"
|
||||||
rtest "github.com/hashicorp/consul/internal/resource/resourcetest"
|
rtest "github.com/hashicorp/consul/internal/resource/resourcetest"
|
||||||
"github.com/hashicorp/consul/internal/tenancy"
|
|
||||||
"github.com/hashicorp/consul/internal/tenancy/internal/controllers/common"
|
"github.com/hashicorp/consul/internal/tenancy/internal/controllers/common"
|
||||||
"github.com/hashicorp/consul/internal/tenancy/internal/controllers/namespace"
|
"github.com/hashicorp/consul/internal/tenancy/internal/controllers/namespace"
|
||||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||||
|
@ -34,21 +32,18 @@ type nsTestSuite struct {
|
||||||
client *rtest.Client
|
client *rtest.Client
|
||||||
runtime controller.Runtime
|
runtime controller.Runtime
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
registry resource.Registry
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ts *nsTestSuite) SetupTest() {
|
func (ts *nsTestSuite) SetupTest() {
|
||||||
ts.registry = resource.NewRegistry()
|
builder := svctest.NewResourceServiceBuilder().
|
||||||
tenancyBridge := tenancy.NewV2TenancyBridge()
|
WithV2Tenancy(true).
|
||||||
config := svc.Config{TenancyBridge: tenancyBridge, Registry: ts.registry, UseV2Tenancy: true}
|
WithRegisterFns(demo.RegisterTypes)
|
||||||
resourceClient := svctest.RunResourceServiceWithConfig(ts.T(), config, tenancy.RegisterTypes, demo.RegisterTypes)
|
ts.client = rtest.NewClient(builder.Run(ts.T()))
|
||||||
tenancyBridge.WithClient(resourceClient)
|
ts.runtime = controller.Runtime{Client: ts.client, Logger: testutil.Logger(ts.T())}
|
||||||
ts.client = rtest.NewClient(resourceClient)
|
|
||||||
ts.runtime = controller.Runtime{Client: resourceClient, Logger: testutil.Logger(ts.T())}
|
|
||||||
ts.ctx = testutil.TestContext(ts.T())
|
ts.ctx = testutil.TestContext(ts.T())
|
||||||
|
|
||||||
mgr := controller.NewManager(ts.client, testutil.Logger(ts.T()))
|
mgr := controller.NewManager(ts.client, testutil.Logger(ts.T()))
|
||||||
mgr.Register(namespace.Controller(ts.registry))
|
mgr.Register(namespace.Controller(builder.Registry()))
|
||||||
mgr.SetRaftLeader(true)
|
mgr.SetRaftLeader(true)
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
ts.T().Cleanup(cancel)
|
ts.T().Cleanup(cancel)
|
||||||
|
|
|
@ -11,21 +11,16 @@ import (
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
svc "github.com/hashicorp/consul/agent/grpc-external/services/resource"
|
|
||||||
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
|
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
|
||||||
"github.com/hashicorp/consul/internal/resource"
|
"github.com/hashicorp/consul/internal/resource"
|
||||||
rtest "github.com/hashicorp/consul/internal/resource/resourcetest"
|
rtest "github.com/hashicorp/consul/internal/resource/resourcetest"
|
||||||
"github.com/hashicorp/consul/internal/tenancy"
|
|
||||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||||
pbtenancy "github.com/hashicorp/consul/proto-public/pbtenancy/v2beta1"
|
pbtenancy "github.com/hashicorp/consul/proto-public/pbtenancy/v2beta1"
|
||||||
"github.com/hashicorp/consul/proto/private/prototest"
|
"github.com/hashicorp/consul/proto/private/prototest"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestWriteNamespace_Success(t *testing.T) {
|
func TestWriteNamespace_Success(t *testing.T) {
|
||||||
v2TenancyBridge := tenancy.NewV2TenancyBridge()
|
cl := rtest.NewClient(svctest.NewResourceServiceBuilder().WithV2Tenancy(true).Run(t))
|
||||||
config := svc.Config{TenancyBridge: v2TenancyBridge, UseV2Tenancy: true}
|
|
||||||
client := svctest.RunResourceServiceWithConfig(t, config, tenancy.RegisterTypes)
|
|
||||||
cl := rtest.NewClient(client)
|
|
||||||
|
|
||||||
res := rtest.Resource(pbtenancy.NamespaceType, "ns1").
|
res := rtest.Resource(pbtenancy.NamespaceType, "ns1").
|
||||||
WithTenancy(resource.DefaultPartitionedTenancy()).
|
WithTenancy(resource.DefaultPartitionedTenancy()).
|
||||||
|
@ -41,10 +36,7 @@ func TestWriteNamespace_Success(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReadNamespace_Success(t *testing.T) {
|
func TestReadNamespace_Success(t *testing.T) {
|
||||||
v2TenancyBridge := tenancy.NewV2TenancyBridge()
|
cl := rtest.NewClient(svctest.NewResourceServiceBuilder().WithV2Tenancy(true).Run(t))
|
||||||
config := svc.Config{TenancyBridge: v2TenancyBridge, UseV2Tenancy: true}
|
|
||||||
client := svctest.RunResourceServiceWithConfig(t, config, tenancy.RegisterTypes)
|
|
||||||
cl := rtest.NewClient(client)
|
|
||||||
|
|
||||||
res := rtest.Resource(pbtenancy.NamespaceType, "ns1").
|
res := rtest.Resource(pbtenancy.NamespaceType, "ns1").
|
||||||
WithData(t, validNamespace()).
|
WithData(t, validNamespace()).
|
||||||
|
@ -74,10 +66,7 @@ func TestReadNamespace_Success(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeleteNamespace_Success(t *testing.T) {
|
func TestDeleteNamespace_Success(t *testing.T) {
|
||||||
v2TenancyBridge := tenancy.NewV2TenancyBridge()
|
cl := rtest.NewClient(svctest.NewResourceServiceBuilder().WithV2Tenancy(true).Run(t))
|
||||||
config := svc.Config{TenancyBridge: v2TenancyBridge, UseV2Tenancy: true}
|
|
||||||
client := svctest.RunResourceServiceWithConfig(t, config, tenancy.RegisterTypes)
|
|
||||||
cl := rtest.NewClient(client)
|
|
||||||
|
|
||||||
res := rtest.Resource(pbtenancy.NamespaceType, "ns1").
|
res := rtest.Resource(pbtenancy.NamespaceType, "ns1").
|
||||||
WithData(t, validNamespace()).Write(t, cl)
|
WithData(t, validNamespace()).Write(t, cl)
|
||||||
|
@ -96,10 +85,7 @@ func TestDeleteNamespace_Success(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestListNamespace_Success(t *testing.T) {
|
func TestListNamespace_Success(t *testing.T) {
|
||||||
v2TenancyBridge := tenancy.NewV2TenancyBridge()
|
cl := rtest.NewClient(svctest.NewResourceServiceBuilder().WithV2Tenancy(true).Run(t))
|
||||||
config := svc.Config{TenancyBridge: v2TenancyBridge, UseV2Tenancy: true}
|
|
||||||
client := svctest.RunResourceServiceWithConfig(t, config, tenancy.RegisterTypes)
|
|
||||||
cl := rtest.NewClient(client)
|
|
||||||
|
|
||||||
res := rtest.Resource(pbtenancy.NamespaceType, "ns1").
|
res := rtest.Resource(pbtenancy.NamespaceType, "ns1").
|
||||||
WithData(t, validNamespace()).Write(t, cl)
|
WithData(t, validNamespace()).Write(t, cl)
|
||||||
|
|
Loading…
Reference in New Issue