Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions internal/resource/registry.go
Original file line number Diff line number Diff line change
@@ -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)
}
89 changes: 89 additions & 0 deletions internal/resource/registry_test.go
Original file line number Diff line number Diff line change
@@ -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)
}