mirror of https://github.com/hashicorp/consul
Basic resource type registry (#16622)
parent
8f75d99299
commit
5a3fec6238
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
Loading…
Reference in new issue