Skip to content

Commit 0f83fcb

Browse files
authored
feat(components): define registry interface for components init simplification (#650)
Address #511. --------- Signed-off-by: Gyuho Lee <[email protected]>
1 parent 0343e3c commit 0f83fcb

File tree

2 files changed

+146
-0
lines changed

2 files changed

+146
-0
lines changed

components/registry.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package components
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"sync"
7+
8+
"github.com/leptonai/gpud/pkg/eventstore"
9+
pkghost "github.com/leptonai/gpud/pkg/host"
10+
nvidianvml "github.com/leptonai/gpud/pkg/nvidia-query/nvml"
11+
)
12+
13+
// GPUdInstance is the instance of the GPUd dependencies.
14+
type GPUdInstance struct {
15+
RootCtx context.Context
16+
17+
NVMLInstance nvidianvml.InstanceV2
18+
EventStore eventstore.Store
19+
RebootEventStore pkghost.RebootEventStore
20+
}
21+
22+
// Registry is the interface for the registry of components.
23+
type Registry interface {
24+
// MustRegister registers a component with the given name and initialization function.
25+
// It panics if the component is already registered.
26+
// It panics if the initialization function returns an error.
27+
MustRegister(name string, initFunc func(GPUdInstance) (Component, error))
28+
}
29+
30+
var _ Registry = &registry{}
31+
32+
type registry struct {
33+
mu sync.RWMutex
34+
gpudInstance GPUdInstance
35+
components map[string]Component
36+
}
37+
38+
// NewRegistry creates a new registry.
39+
func NewRegistry(gpudInstance GPUdInstance) *registry {
40+
return &registry{
41+
gpudInstance: gpudInstance,
42+
components: make(map[string]Component),
43+
}
44+
}
45+
46+
// MustRegister registers a component with the given name and initialization function.
47+
// It panics if the component is already registered.
48+
// It panics if the initialization function returns an error.
49+
func (r *registry) MustRegister(name string, initFunc func(GPUdInstance) (Component, error)) {
50+
if err := r.registerInit(name, initFunc); err != nil {
51+
panic(err)
52+
}
53+
}
54+
55+
// hasRegistered checks if a component with the given name is already registered.
56+
func (r *registry) hasRegistered(name string) bool {
57+
r.mu.RLock()
58+
defer r.mu.RUnlock()
59+
60+
_, ok := r.components[name]
61+
return ok
62+
}
63+
64+
// registerInit registers an initialization function for a component with the given name.
65+
func (r *registry) registerInit(name string, initFunc func(GPUdInstance) (Component, error)) error {
66+
if r.hasRegistered(name) {
67+
return fmt.Errorf("component %s already registered", name)
68+
}
69+
70+
c, err := initFunc(r.gpudInstance)
71+
if err != nil {
72+
return err
73+
}
74+
75+
r.mu.Lock()
76+
r.components[name] = c
77+
r.mu.Unlock()
78+
79+
return nil
80+
}

components/registry_test.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package components
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
// Mock function that returns a component without error
12+
func mockInitFuncSuccess(instance GPUdInstance) (Component, error) {
13+
return newMockComponent("test-component"), nil
14+
}
15+
16+
// Mock function that returns an error
17+
func mockInitFuncError(instance GPUdInstance) (Component, error) {
18+
return nil, fmt.Errorf("mock init error")
19+
}
20+
21+
func TestHasRegistered(t *testing.T) {
22+
// Create a new registry
23+
reg := NewRegistry(GPUdInstance{
24+
RootCtx: context.Background(),
25+
})
26+
27+
// When registry is empty, should return false
28+
assert.False(t, reg.hasRegistered("test-component"))
29+
30+
// Register a component
31+
comp := newMockComponent("test-component")
32+
reg.mu.Lock()
33+
reg.components["test-component"] = comp
34+
reg.mu.Unlock()
35+
36+
// Should now return true for the registered component
37+
assert.True(t, reg.hasRegistered("test-component"))
38+
39+
// Should still return false for unregistered components
40+
assert.False(t, reg.hasRegistered("unknown-component"))
41+
}
42+
43+
func TestRegisterInitFunc(t *testing.T) {
44+
// Create a new registry
45+
reg := NewRegistry(GPUdInstance{
46+
RootCtx: context.Background(),
47+
})
48+
49+
// Test registering a component successfully
50+
err := reg.registerInit("test-component", mockInitFuncSuccess)
51+
assert.NoError(t, err)
52+
assert.True(t, reg.hasRegistered("test-component"))
53+
54+
// Test registering a component that already exists
55+
err = reg.registerInit("test-component", mockInitFuncSuccess)
56+
assert.Error(t, err)
57+
assert.Contains(t, err.Error(), "already registered")
58+
59+
// Test registering a component with an initialization function that returns an error
60+
err = reg.registerInit("error-component", mockInitFuncError)
61+
assert.Error(t, err)
62+
assert.Contains(t, err.Error(), "mock init error")
63+
64+
// The component should not be registered if the init function fails
65+
assert.False(t, reg.hasRegistered("error-component"))
66+
}

0 commit comments

Comments
 (0)