-
Notifications
You must be signed in to change notification settings - Fork 4.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add namespace proto and registration (#18848)
* add namespace proto and registration * fix proto generation * add missing copywrite headers * fix proto linter errors * fix exports and Type export * add mutate hook and more validation * add more validation rules and tests * Apply suggestions from code review Co-authored-by: Semir Patel <[email protected]> * fix owner error and add test * remove ACL for now * add tests around space suffix prefix. * only fait when ns and ap are default, add test for it --------- Co-authored-by: Semir Patel <[email protected]>
- Loading branch information
1 parent
9e3794e
commit 341dc28
Showing
9 changed files
with
468 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: BUSL-1.1 | ||
|
||
package tenancy | ||
|
||
import ( | ||
"github.com/hashicorp/consul/internal/resource" | ||
"github.com/hashicorp/consul/internal/tenancy/internal/types" | ||
) | ||
|
||
var ( | ||
// API Group Information | ||
|
||
APIGroup = types.GroupName | ||
VersionV1Alpha1 = types.VersionV1Alpha1 | ||
CurrentVersion = types.CurrentVersion | ||
|
||
// Resource Kind Names. | ||
|
||
NamespaceKind = types.NamespaceKind | ||
NamespaceV1Alpha1Type = types.NamespaceV1Alpha1Type | ||
) | ||
|
||
// RegisterTypes adds all resource types within the "tenancy" API group | ||
// to the given type registry | ||
func RegisterTypes(r resource.Registry) { | ||
types.Register(r) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: BUSL-1.1 | ||
|
||
package types | ||
|
||
import "errors" | ||
|
||
var ( | ||
errInvalidName = errors.New("invalid namespace name provided") | ||
errOwnerNonEmpty = errors.New("namespace should not have an owner") | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: BUSL-1.1 | ||
|
||
package types | ||
|
||
import ( | ||
"fmt" | ||
"github.com/hashicorp/consul/agent/dns" | ||
"github.com/hashicorp/consul/internal/resource" | ||
"github.com/hashicorp/consul/proto-public/pbresource" | ||
tenancyv1alpha1 "github.com/hashicorp/consul/proto-public/pbtenancy/v1alpha1" | ||
"strings" | ||
) | ||
|
||
const ( | ||
NamespaceKind = "Namespace" | ||
) | ||
|
||
var ( | ||
NamespaceV1Alpha1Type = &pbresource.Type{ | ||
Group: GroupName, | ||
GroupVersion: VersionV1Alpha1, | ||
Kind: NamespaceKind, | ||
} | ||
NamespaceType = NamespaceV1Alpha1Type | ||
) | ||
|
||
func RegisterNamespace(r resource.Registry) { | ||
r.Register(resource.Registration{ | ||
Type: NamespaceV1Alpha1Type, | ||
Proto: &tenancyv1alpha1.Namespace{}, | ||
Scope: resource.ScopePartition, | ||
Validate: ValidateNamespace, | ||
Mutate: MutateNamespace, | ||
}) | ||
} | ||
|
||
func MutateNamespace(res *pbresource.Resource) error { | ||
res.Id.Name = strings.ToLower(res.Id.Name) | ||
return nil | ||
} | ||
|
||
func ValidateNamespace(res *pbresource.Resource) error { | ||
var ns tenancyv1alpha1.Namespace | ||
|
||
if err := res.Data.UnmarshalTo(&ns); err != nil { | ||
return resource.NewErrDataParse(&ns, err) | ||
} | ||
if res.Owner != nil { | ||
return errOwnerNonEmpty | ||
} | ||
|
||
// it's not allowed to create default/default tenancy | ||
if res.Id.Name == resource.DefaultNamespaceName && res.Id.Tenancy.Partition == resource.DefaultPartitionName { | ||
return errInvalidName | ||
} | ||
|
||
if !dns.IsValidLabel(res.Id.Name) { | ||
return fmt.Errorf("namespace name %q is not a valid DNS hostname", res.Id.Name) | ||
} | ||
|
||
switch strings.ToLower(res.Id.Name) { | ||
case "system", "universal", "operator", "root": | ||
return fmt.Errorf("namespace %q is reserved for future internal use", res.Id.Name) | ||
default: | ||
|
||
return nil | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: BUSL-1.1 | ||
|
||
package types | ||
|
||
import ( | ||
"errors" | ||
"github.com/hashicorp/consul/internal/resource" | ||
pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v1alpha1" | ||
"github.com/hashicorp/consul/proto-public/pbresource" | ||
tenancyv1alpha1 "github.com/hashicorp/consul/proto-public/pbtenancy/v1alpha1" | ||
"github.com/stretchr/testify/require" | ||
"google.golang.org/protobuf/reflect/protoreflect" | ||
"google.golang.org/protobuf/types/known/anypb" | ||
"testing" | ||
) | ||
|
||
func createNamespaceResource(t *testing.T, data protoreflect.ProtoMessage) *pbresource.Resource { | ||
res := &pbresource.Resource{ | ||
Id: &pbresource.ID{ | ||
Type: NamespaceV1Alpha1Type, | ||
Tenancy: resource.DefaultPartitionedTenancy(), | ||
Name: "ns1234", | ||
}, | ||
} | ||
|
||
var err error | ||
res.Data, err = anypb.New(data) | ||
require.NoError(t, err) | ||
return res | ||
} | ||
|
||
func validNamespace() *tenancyv1alpha1.Namespace { | ||
return &tenancyv1alpha1.Namespace{ | ||
Description: "description from user", | ||
} | ||
} | ||
|
||
func TestValidateNamespace_Ok(t *testing.T) { | ||
res := createNamespaceResource(t, validNamespace()) | ||
|
||
err := ValidateNamespace(res) | ||
require.NoError(t, err) | ||
} | ||
|
||
func TestValidateNamespace_defaultNamespace(t *testing.T) { | ||
res := createNamespaceResource(t, validNamespace()) | ||
res.Id.Name = resource.DefaultNamespaceName | ||
|
||
err := ValidateNamespace(res) | ||
require.Error(t, err) | ||
require.ErrorAs(t, err, &errInvalidName) | ||
} | ||
|
||
func TestValidateNamespace_defaultNamespaceNonDefaultPartition(t *testing.T) { | ||
res := createNamespaceResource(t, validNamespace()) | ||
res.Id.Name = resource.DefaultNamespaceName | ||
res.Id.Tenancy.Partition = "foo" | ||
|
||
err := ValidateNamespace(res) | ||
require.NoError(t, err) | ||
} | ||
|
||
func TestValidateNamespace_InvalidName(t *testing.T) { | ||
res := createNamespaceResource(t, validNamespace()) | ||
res.Id.Name = "-invalid" | ||
|
||
err := ValidateNamespace(res) | ||
require.Error(t, err) | ||
require.ErrorAs(t, err, &errInvalidName) | ||
} | ||
|
||
func TestValidateNamespace_InvalidOwner(t *testing.T) { | ||
res := createNamespaceResource(t, validNamespace()) | ||
res.Owner = &pbresource.ID{} | ||
err := ValidateNamespace(res) | ||
|
||
require.Error(t, err) | ||
require.ErrorAs(t, err, &errOwnerNonEmpty) | ||
} | ||
|
||
func TestValidateNamespace_ParseError(t *testing.T) { | ||
// Any type other than the Namespace type would work | ||
// to cause the error we are expecting | ||
data := &pbcatalog.IP{Address: "198.18.0.1"} | ||
|
||
res := createNamespaceResource(t, data) | ||
|
||
err := ValidateNamespace(res) | ||
require.Error(t, err) | ||
require.ErrorAs(t, err, &resource.ErrDataParse{}) | ||
} | ||
|
||
func TestMutateNamespace(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
namespaceName string | ||
expectedName string | ||
err error | ||
}{ | ||
{"lower", "lower", "lower", nil}, | ||
{"mixed", "MiXeD", "mixed", nil}, | ||
{"upper", "UPPER", "upper", nil}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
res := &pbresource.Resource{Id: &pbresource.ID{Name: tt.namespaceName}} | ||
if err := MutateNamespace(res); !errors.Is(err, tt.err) { | ||
t.Errorf("MutateNamespace() error = %v", err) | ||
} | ||
require.Equal(t, res.Id.Name, tt.expectedName) | ||
}) | ||
} | ||
} | ||
|
||
func TestValidateNamespace(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
namespaceName string | ||
err string | ||
}{ | ||
{"system", "System", "namespace \"System\" is reserved for future internal use"}, | ||
{"invalid", "-inval", "namespace name \"-inval\" is not a valid DNS hostname"}, | ||
{"valid", "ns1", ""}, | ||
{"space prefix", " foo", "namespace name \" foo\" is not a valid DNS hostname"}, | ||
{"space suffix", "bar ", "namespace name \"bar \" is not a valid DNS hostname"}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
a, err := anypb.New(&tenancyv1alpha1.Namespace{}) | ||
require.NoError(t, err) | ||
res := &pbresource.Resource{Id: &pbresource.ID{Name: tt.namespaceName}, Data: a} | ||
err = ValidateNamespace(res) | ||
if tt.err == "" { | ||
require.NoError(t, err) | ||
} else { | ||
require.Equal(t, err.Error(), tt.err) | ||
} | ||
|
||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: BUSL-1.1 | ||
|
||
package types | ||
|
||
import ( | ||
"github.com/hashicorp/consul/internal/resource" | ||
) | ||
|
||
const ( | ||
GroupName = "tenancy" | ||
VersionV1Alpha1 = "v1alpha1" | ||
CurrentVersion = VersionV1Alpha1 | ||
) | ||
|
||
func Register(r resource.Registry) { | ||
RegisterNamespace(r) | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.