Skip to content

Commit 9999008

Browse files
Implement Registry (#11)
1 parent e8c5968 commit 9999008

File tree

4 files changed

+389
-43
lines changed

4 files changed

+389
-43
lines changed

internal/runtime/client/client.go

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,12 @@ import (
3333

3434
// Options are creation options for a Client.
3535
type Options struct {
36-
Catalog *catalog.Catalog
37-
Registry registry.Registry
36+
Catalog *catalog.Catalog
3837
}
3938

4039
func New(options Options) Client {
4140
return &client{
42-
catalog: options.Catalog,
43-
registry: options.Registry,
41+
catalog: options.Catalog,
4442
}
4543
}
4644

@@ -56,8 +54,7 @@ type Client interface {
5654
}
5755

5856
type client struct {
59-
catalog *catalog.Catalog
60-
registry registry.Registry
57+
catalog *catalog.Catalog
6158

6259
host string
6360
basePath string
@@ -106,12 +103,12 @@ type hookClient struct {
106103
}
107104

108105
func (h hookClient) Call(ctx context.Context, name string, in, out runtime.Object) error {
109-
gvh, err := h.client.catalog.GroupVersionHook(h.hook)
106+
_, err := h.client.catalog.GroupVersionHook(h.hook)
110107
if err != nil {
111108
return err
112109
}
113110

114-
registration := h.client.registry.GetRuntimeExtension(gvh, name)
111+
registration, _ := registry.Extensions().Get(name)
115112

116113
c := createHttpClient(registration)
117114

@@ -124,7 +121,7 @@ func (h hookClient) CallAll(ctx context.Context, in, out runtime.Object) error {
124121
return err
125122
}
126123

127-
registrations := h.client.registry.GetRuntimeExtensions(gvh)
124+
registrations, _ := registry.Extensions().List(gvh)
128125
for _, registration := range registrations {
129126
c := createHttpClient(registration)
130127

@@ -136,7 +133,7 @@ func (h hookClient) CallAll(ctx context.Context, in, out runtime.Object) error {
136133
return nil
137134
}
138135

139-
func createHttpClient(registration registry.RuntimeExtensionRegistration) httpClient {
136+
func createHttpClient(registration *registry.RuntimeExtensionRegistration) httpClient {
140137
return httpClient{}
141138
}
142139

internal/runtime/registry/registry.go

Lines changed: 197 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,224 @@
1+
/*
2+
Copyright 2022 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
117
package registry
218

319
import (
4-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
20+
"sync"
21+
22+
"github.com/pkg/errors"
23+
"k8s.io/apimachinery/pkg/runtime/schema"
524

625
runtimev1 "sigs.k8s.io/cluster-api/exp/runtime/api/v1beta1"
726
"sigs.k8s.io/cluster-api/internal/runtime/catalog"
827
)
928

10-
func New() Registry {
11-
return &registry{
12-
registrations: map[string]RuntimeExtensionRegistration{},
13-
}
14-
}
29+
// TODO: handle namespace selector
30+
31+
var (
32+
once sync.Once
33+
instance ExtensionRegistry
34+
)
35+
36+
// ExtensionRegistry defines all the func an extension registry supports.
37+
type ExtensionRegistry interface {
38+
// WarmUp can be used to initialise a "cold" extension registry with all the known extensions at a given time.
39+
// After WarmUp completes the extension registry is considered ready.
40+
WarmUp(ext *runtimev1.ExtensionList) error
41+
42+
// IsReady returns true if the extension registry is ready for usage, and this happens
43+
// after WarmUp is completed.
44+
IsReady() bool
45+
46+
// Add all the Runtime Extensions served by a registered runtime extension server.
47+
// Please note that if the provided registration object already exists, all the registered
48+
// RuntimeExtensions gets updated according to the newly provide status.
49+
Add(ext *runtimev1.Extension) error
50+
51+
// Remove all the Runtime Extensions served by a registered runtime extension server.
52+
Remove(ext *runtimev1.Extension) error
53+
54+
// List all the registered Runtime Extensions for a given Group/Hook.
55+
List(gh catalog.GroupVersionHook) ([]*RuntimeExtensionRegistration, error)
1556

16-
type registry struct {
17-
registrations map[string]RuntimeExtensionRegistration
57+
// Get the Runtime Extensions with a given name
58+
Get(name string) (*RuntimeExtensionRegistration, error)
1859
}
1960

61+
// RuntimeExtensionRegistration contain info about a registered Runtime Extension.
2062
type RuntimeExtensionRegistration struct {
21-
Name string
63+
// RegistrationName is the name of the object who originated the registration of this Runtime Extension.
64+
RegistrationName string
65+
66+
// The unique name of the Runtime Extension.
67+
Name string
68+
69+
// The GroupVersionHook the Runtime Extension implements.
2270
GroupVersionHook catalog.GroupVersionHook
23-
ClientConfig ClientConfig
71+
72+
ClientConfig runtimev1.WebhookClientConfig
73+
TimeoutSeconds *int32
74+
FailurePolicy *runtimev1.FailurePolicyType
75+
}
76+
77+
type extensionRegistry struct {
78+
ready bool
79+
items map[string]*RuntimeExtensionRegistration
80+
lock sync.RWMutex
81+
}
82+
83+
// Extensions provide access to all the registered Runtime Extensions.
84+
func Extensions() ExtensionRegistry {
85+
once.Do(func() {
86+
instance = extensions()
87+
})
88+
return instance
89+
}
90+
91+
func extensions() ExtensionRegistry {
92+
return &extensionRegistry{
93+
items: map[string]*RuntimeExtensionRegistration{},
94+
}
95+
}
96+
97+
// WarmUp can be used to initialise a "cold" extension registry with all the known extensions at a given time.
98+
// After WarmUp completes the extension registry is considered ready.
99+
func (extensions *extensionRegistry) WarmUp(extList *runtimev1.ExtensionList) error {
100+
if extList == nil {
101+
return errors.New("invalid argument, when calling WarmUp extList must not be nil")
102+
}
103+
104+
extensions.lock.Lock()
105+
defer extensions.lock.Unlock()
106+
107+
for i := range extList.Items {
108+
if err := extensions.add(&extList.Items[i]); err != nil { // TODO: consider if to aggregate errors
109+
return err
110+
}
111+
}
112+
113+
extensions.ready = true
114+
return nil
24115
}
25116

26-
type ClientConfig struct {
27-
WebhookClientConfig runtimev1.WebhookClientConfig
28-
TimeoutSeconds *int32
29-
FailurePolicy *runtimev1.FailurePolicyType
30-
NamespaceSelector *metav1.LabelSelector
117+
// IsReady returns true if the extension registry is ready for usage, and this happens
118+
// after WarmUp is completed.
119+
func (extensions *extensionRegistry) IsReady() bool {
120+
extensions.lock.RLock()
121+
defer extensions.lock.RUnlock()
122+
123+
return extensions.ready
31124
}
32125

33-
func (r registry) RegisterRuntimeExtension(ext *runtimev1.Extension) {
34-
panic("implement me")
126+
// Add all the Runtime Extensions served by a registered runtime extension server.
127+
// Please note that if the provided registration object already exists, all the registered
128+
// RuntimeExtensions gets updated according to the newly provide status.
129+
func (extensions *extensionRegistry) Add(ext *runtimev1.Extension) error {
130+
if !extensions.ready {
131+
return errors.New("invalid operation: Get cannot called on a registry not yet ready")
132+
}
133+
134+
if ext == nil {
135+
return errors.New("invalid argument, when calling Add ext must not be nil")
136+
}
137+
138+
extensions.lock.Lock()
139+
defer extensions.lock.Unlock()
140+
return extensions.add(ext)
35141
}
36142

37-
func (r registry) RemoveRuntimeExtension(ext *runtimev1.Extension) {
38-
panic("implement me")
143+
// Remove all the Runtime Extensions served by a registered runtime extension server.
144+
func (extensions *extensionRegistry) Remove(ext *runtimev1.Extension) error {
145+
if !extensions.ready {
146+
return errors.New("invalid operation: Get cannot called on a registry not yet ready")
147+
}
148+
149+
if ext == nil {
150+
return errors.New("invalid argument, when calling Remove ext must not be nil")
151+
}
152+
153+
extensions.lock.Lock()
154+
defer extensions.lock.Unlock()
155+
156+
extensions.remove(ext)
157+
return nil
39158
}
40159

41-
func (r registry) GetRuntimeExtension(gvh catalog.GroupVersionHook, name string) RuntimeExtensionRegistration {
42-
panic("implement me")
160+
func (extensions *extensionRegistry) remove(ext *runtimev1.Extension) {
161+
for _, e := range extensions.items {
162+
if e.RegistrationName == ext.Name {
163+
delete(extensions.items, e.Name)
164+
}
165+
}
43166
}
44167

45-
func (r registry) GetRuntimeExtensions(gvh catalog.GroupVersionHook) []RuntimeExtensionRegistration {
46-
panic("implement me")
168+
// List all the registered Runtime Extensions for a given Group/Hook..
169+
func (extensions *extensionRegistry) List(gh catalog.GroupVersionHook) ([]*RuntimeExtensionRegistration, error) {
170+
if !extensions.ready {
171+
return nil, errors.New("invalid operation: Get cannot called on a registry not yet ready")
172+
}
173+
174+
extensions.lock.RLock()
175+
defer extensions.lock.RUnlock()
176+
177+
l := make([]*RuntimeExtensionRegistration, 0, 10)
178+
for _, r := range extensions.items {
179+
if r.GroupVersionHook.Group == gh.Group && r.GroupVersionHook.Hook == gh.Hook {
180+
l = append(l, r)
181+
}
182+
}
183+
return l, nil
47184
}
48185

49-
type Registry interface {
50-
RemoveRuntimeExtension(ext *runtimev1.Extension)
51-
RegisterRuntimeExtension(ext *runtimev1.Extension)
186+
// Get the Runtime Extensions with a given name.
187+
func (extensions *extensionRegistry) Get(name string) (*RuntimeExtensionRegistration, error) {
188+
if !extensions.ready {
189+
return nil, errors.New("invalid operation: Get cannot called on a registry not yet ready")
190+
}
191+
192+
extensions.lock.RLock()
193+
defer extensions.lock.RUnlock()
194+
195+
r, _ := extensions.items[name]
196+
return r, nil
197+
}
198+
199+
func (extensions *extensionRegistry) add(ext *runtimev1.Extension) error {
200+
extensions.remove(ext)
201+
202+
for _, e := range ext.Status.RuntimeExtensions {
203+
gv, err := schema.ParseGroupVersion(e.Hook.APIVersion)
204+
if err != nil { // TODO: consider if to aggregate errors
205+
return errors.Wrapf(err, "failed to parse GroupVersion from %q", e.Hook.APIVersion)
206+
}
207+
208+
r := &RuntimeExtensionRegistration{
209+
RegistrationName: ext.Name,
210+
Name: e.Name,
211+
GroupVersionHook: catalog.GroupVersionHook{
212+
Group: gv.Group,
213+
Version: gv.Version,
214+
Hook: e.Hook.Name,
215+
},
216+
ClientConfig: ext.Spec.ClientConfig,
217+
TimeoutSeconds: e.TimeoutSeconds,
218+
FailurePolicy: e.FailurePolicy,
219+
}
220+
extensions.items[r.Name] = r
221+
}
52222

53-
GetRuntimeExtension(gvh catalog.GroupVersionHook, name string) RuntimeExtensionRegistration
54-
GetRuntimeExtensions(gvh catalog.GroupVersionHook) []RuntimeExtensionRegistration
223+
return nil
55224
}

0 commit comments

Comments
 (0)