Basic resource type registry (#16622)

This commit is contained in:
Semir Patel 2023-03-14 13:30:25 -05:00 committed by GitHub
parent f3be5d9b80
commit f0c36029b8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 160 additions and 0 deletions

View file

@ -0,0 +1,71 @@
package resource
import (
"fmt"
"sync"
"github.com/hashicorp/consul/proto-public/pbresource"
)
type Registry interface {
// Register the given resource type and its hooks.
Register(reg Registration)
// Resolve the given resource type and its hooks.
Resolve(typ *pbresource.Type) (reg Registration, ok bool)
}
type Registration struct {
// Type is the GVK of the resource type.
Type *pbresource.Type
// In the future, we'll add hooks, the controller etc. here.
// TODO: https://github.com/hashicorp/consul/pull/16622#discussion_r1134515909
}
// Hashable key for a resource type
type TypeKey string
// Resource type registry
type TypeRegistry struct {
// registrations keyed by GVK
registrations map[string]Registration
lock sync.RWMutex
}
func NewRegistry() Registry {
return &TypeRegistry{
registrations: make(map[string]Registration),
}
}
func (r *TypeRegistry) Register(registration Registration) {
r.lock.Lock()
defer r.lock.Unlock()
typ := registration.Type
if typ.Group == "" || typ.GroupVersion == "" || typ.Kind == "" {
panic("type field(s) cannot be empty")
}
key := ToGVK(registration.Type)
if _, ok := r.registrations[key]; ok {
panic(fmt.Sprintf("resource type %s already registered", key))
}
r.registrations[key] = registration
}
func (r *TypeRegistry) Resolve(typ *pbresource.Type) (reg Registration, ok bool) {
r.lock.RLock()
defer r.lock.RUnlock()
if registration, ok := r.registrations[ToGVK(typ)]; ok {
return registration, true
}
return Registration{}, false
}
func ToGVK(resourceType *pbresource.Type) string {
return fmt.Sprintf("%s/%s/%s", resourceType.Group, resourceType.GroupVersion, resourceType.Kind)
}

View file

@ -0,0 +1,89 @@
package resource
import (
"testing"
"github.com/hashicorp/consul/proto-public/pbresource"
"github.com/stretchr/testify/assert"
)
func TestRegister(t *testing.T) {
r := NewRegistry()
serviceType := &pbresource.Type{
Group: "mesh",
GroupVersion: "v1",
Kind: "service",
}
// register
serviceRegistration := Registration{Type: serviceType}
r.Register(serviceRegistration)
// register existing should panic
assertRegisterPanics(t, r.Register, serviceRegistration, "resource type mesh/v1/service already registered")
// register empty Group should panic
assertRegisterPanics(t, r.Register, Registration{
Type: &pbresource.Type{
Group: "",
GroupVersion: "v1",
Kind: "service",
},
}, "type field(s) cannot be empty")
// register empty GroupVersion should panic
assertRegisterPanics(t, r.Register, Registration{
Type: &pbresource.Type{
Group: "mesh",
GroupVersion: "",
Kind: "service",
},
}, "type field(s) cannot be empty")
// register empty Kind should panic
assertRegisterPanics(t, r.Register, Registration{
Type: &pbresource.Type{
Group: "mesh",
GroupVersion: "v1",
Kind: "",
},
}, "type field(s) cannot be empty")
}
func assertRegisterPanics(t *testing.T, registerFn func(reg Registration), registration Registration, panicString string) {
defer func() {
if r := recover(); r == nil {
t.Errorf("expected panic, but none occurred")
} else {
errstr, ok := r.(string)
if !ok {
t.Errorf("unexpected error type returned from panic")
} else if errstr != panicString {
t.Errorf("expected %s error message but got: %s", panicString, errstr)
}
}
}()
registerFn(registration)
}
func TestResolve(t *testing.T) {
r := NewRegistry()
serviceType := &pbresource.Type{
Group: "mesh",
GroupVersion: "v1",
Kind: "service",
}
// not found
_, ok := r.Resolve(serviceType)
assert.False(t, ok)
// found
r.Register(Registration{Type: serviceType})
registration, ok := r.Resolve(serviceType)
assert.True(t, ok)
assert.Equal(t, registration.Type, serviceType)
}