From cd10eb8496f8fabd26b0375172f2fdba5e260b75 Mon Sep 17 00:00:00 2001 From: Andreas Fritzler Date: Tue, 10 Aug 2021 21:51:21 +0200 Subject: [PATCH] Externalized owner cache into own package and added basic test suite setup --- pkg/ipam/{init_test.go => suite_test.go} | 0 pkg/manager/manager.go | 20 ++++-- pkg/ownercache/interface.go | 61 +++++++++++++++++++ pkg/{manager => ownercache}/ownercache.go | 44 ++++++------- .../ownercachereconciler.go | 6 +- pkg/ownercache/suite_test.go | 28 +++++++++ pkg/trigger/interface.go | 42 +++++++++++++ pkg/{manager => trigger}/ready.go | 2 +- pkg/trigger/suite_test.go | 28 +++++++++ pkg/{manager => trigger}/trigger.go | 37 ++++++----- pkg/usagecache/usagecache.go | 4 +- pkg/utils/suite_test.go | 28 +++++++++ 12 files changed, 242 insertions(+), 58 deletions(-) rename pkg/ipam/{init_test.go => suite_test.go} (100%) create mode 100644 pkg/ownercache/interface.go rename pkg/{manager => ownercache}/ownercache.go (75%) rename pkg/{manager => ownercache}/ownercachereconciler.go (97%) create mode 100644 pkg/ownercache/suite_test.go create mode 100644 pkg/trigger/interface.go rename pkg/{manager => trigger}/ready.go (98%) create mode 100644 pkg/trigger/suite_test.go rename pkg/{manager => trigger}/trigger.go (71%) create mode 100644 pkg/utils/suite_test.go diff --git a/pkg/ipam/init_test.go b/pkg/ipam/suite_test.go similarity index 100% rename from pkg/ipam/init_test.go rename to pkg/ipam/suite_test.go diff --git a/pkg/manager/manager.go b/pkg/manager/manager.go index 19b18b795..f4e4a7073 100644 --- a/pkg/manager/manager.go +++ b/pkg/manager/manager.go @@ -17,6 +17,9 @@ package manager import ( + "github.com/onmetal/onmetal-api/pkg/ownercache" + "github.com/onmetal/onmetal-api/pkg/trigger" + "github.com/onmetal/onmetal-api/pkg/usagecache" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/rest" "sigs.k8s.io/controller-runtime/pkg/controller" @@ -25,8 +28,9 @@ import ( type Manager struct { manager.Manager - triggers ReconcilationTrigger - ownerCache *OwnerCache + triggers trigger.ReconcilationTrigger + ownerCache ownercache.OwnerCache + usageCache usagecache.UsageCache } func NewManager(config *rest.Config, options manager.Options) (*Manager, error) { @@ -34,19 +38,25 @@ func NewManager(config *rest.Config, options manager.Options) (*Manager, error) if err != nil { return nil, err } - trig := NewReconcilationTrigger() - oc := NewOwnerCache(mgr, trig) + trig := trigger.NewReconcilationTrigger() + oc := ownercache.NewOwnerCache(mgr, trig) + uc := usagecache.NewUsageCache(mgr, trig) return &Manager{ Manager: mgr, ownerCache: oc, + usageCache: uc, triggers: trig, }, nil } -func (m *Manager) GetOwnerCache() *OwnerCache { +func (m *Manager) GetOwnerCache() ownercache.OwnerCache { return m.ownerCache } +func (m *Manager) GetUsageCache() usagecache.UsageCache { + return m.usageCache +} + func (m *Manager) RegisterControllerFor(gk schema.GroupKind, controller controller.Controller) { m.triggers.RegisterControllerFor(gk, controller) } diff --git a/pkg/ownercache/interface.go b/pkg/ownercache/interface.go new file mode 100644 index 000000000..bef303456 --- /dev/null +++ b/pkg/ownercache/interface.go @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 by the OnMetal authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ownercache + +import ( + "context" + "github.com/onmetal/onmetal-api/pkg/utils" + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/manager" +) + +type OwnerCache interface { + Wait() + RegisterGroupKind(ctx context.Context, gk schema.GroupKind) error + ReplaceObject(object client.Object) (utils.ObjectId, utils.ObjectIds) + + DeleteObject(id utils.ObjectId) utils.ObjectIds + + GetOwnersFor(id utils.ObjectId) utils.ObjectIds + GetOwnersByTypeFor(id utils.ObjectId, gk schema.GroupKind) utils.ObjectIds + GetSerfsFor(id utils.ObjectId) utils.ObjectIds + GetSerfsWithTypeFor(id utils.ObjectId, gk schema.GroupKind) utils.ObjectIds + GetSerfsForObject(obj client.Object) utils.ObjectIds + GetSerfsWithTypeForObject(obj client.Object, gk schema.GroupKind) utils.ObjectIds + + CreateSerf(ctx context.Context, owner, serf client.Object, opts ...client.CreateOption) error +} + +type Trigger interface { + Trigger(id utils.ObjectId) +} + +func NewOwnerCache(manager manager.Manager, trigger Trigger) *ownerCache { + mgr := &ownerCache{ + manager: manager, + trigger: trigger, + client: manager.GetClient(), + registrations: map[schema.GroupKind]*ownerReconciler{}, + owners: map[utils.ObjectId]utils.ObjectIds{}, + serfs: map[utils.ObjectId]utils.ObjectIds{}, + } + if mgr.manager != nil { + mgr.client = manager.GetClient() + } + return mgr +} diff --git a/pkg/manager/ownercache.go b/pkg/ownercache/ownercache.go similarity index 75% rename from pkg/manager/ownercache.go rename to pkg/ownercache/ownercache.go index eaa989ffa..fa7c38b21 100644 --- a/pkg/manager/ownercache.go +++ b/pkg/ownercache/ownercache.go @@ -14,11 +14,12 @@ * limitations under the License. */ -package manager +package ownercache import ( "context" "fmt" + "github.com/onmetal/onmetal-api/pkg/trigger" "sync" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -29,33 +30,22 @@ import ( "github.com/onmetal/onmetal-api/pkg/utils" ) -type OwnerCache struct { +type ownerCache struct { manager manager.Manager - trigger ReconcilationTrigger + trigger Trigger client client.Client registrations map[schema.GroupKind]*ownerReconciler lock sync.RWMutex owners map[utils.ObjectId]utils.ObjectIds serfs map[utils.ObjectId]utils.ObjectIds - ready Ready + ready trigger.Ready } -func NewOwnerCache(manager manager.Manager, trig ReconcilationTrigger) *OwnerCache { - return &OwnerCache{ - manager: manager, - trigger: trig, - client: manager.GetClient(), - registrations: map[schema.GroupKind]*ownerReconciler{}, - owners: map[utils.ObjectId]utils.ObjectIds{}, - serfs: map[utils.ObjectId]utils.ObjectIds{}, - } -} - -func (o *OwnerCache) Wait() { +func (o *ownerCache) Wait() { o.ready.Wait() } -func (o *OwnerCache) RegisterGroupKind(ctx context.Context, gk schema.GroupKind) error { +func (o *ownerCache) RegisterGroupKind(ctx context.Context, gk schema.GroupKind) error { o.lock.Lock() defer o.lock.Unlock() @@ -68,13 +58,13 @@ func (o *OwnerCache) RegisterGroupKind(ctx context.Context, gk schema.GroupKind) return nil } -func (o *OwnerCache) ReplaceObject(object client.Object) (utils.ObjectId, utils.ObjectIds) { +func (o *ownerCache) ReplaceObject(object client.Object) (utils.ObjectId, utils.ObjectIds) { o.lock.Lock() defer o.lock.Unlock() return o.replaceObject(object) } -func (o *OwnerCache) DeleteObject(id utils.ObjectId) utils.ObjectIds { +func (o *ownerCache) DeleteObject(id utils.ObjectId) utils.ObjectIds { o.lock.Lock() defer o.lock.Unlock() old := o.serfs[id] @@ -90,7 +80,7 @@ func (o *OwnerCache) DeleteObject(id utils.ObjectId) utils.ObjectIds { return old } -func (o *OwnerCache) replaceObject(object client.Object) (utils.ObjectId, utils.ObjectIds) { +func (o *ownerCache) replaceObject(object client.Object) (utils.ObjectId, utils.ObjectIds) { id := utils.NewObjectId(object) oids := utils.GetOwnerIdsFor(object) old := o.serfs[id] @@ -123,13 +113,13 @@ func (o *OwnerCache) replaceObject(object client.Object) (utils.ObjectId, utils. return id, oids.Join(old) } -func (o *OwnerCache) GetOwnersFor(id utils.ObjectId) utils.ObjectIds { +func (o *ownerCache) GetOwnersFor(id utils.ObjectId) utils.ObjectIds { o.lock.RLock() defer o.lock.RUnlock() return o.serfs[id].Copy() } -func (o *OwnerCache) GetOwnersByTypeFor(id utils.ObjectId, gk schema.GroupKind) utils.ObjectIds { +func (o *ownerCache) GetOwnersByTypeFor(id utils.ObjectId, gk schema.GroupKind) utils.ObjectIds { o.lock.RLock() defer o.lock.RUnlock() ids := utils.ObjectIds{} @@ -141,13 +131,13 @@ func (o *OwnerCache) GetOwnersByTypeFor(id utils.ObjectId, gk schema.GroupKind) return ids } -func (o *OwnerCache) GetSerfsFor(id utils.ObjectId) utils.ObjectIds { +func (o *ownerCache) GetSerfsFor(id utils.ObjectId) utils.ObjectIds { o.lock.RLock() defer o.lock.RUnlock() return o.owners[id].Copy() } -func (o *OwnerCache) GetSerfsWithTypeFor(id utils.ObjectId, gk schema.GroupKind) utils.ObjectIds { +func (o *ownerCache) GetSerfsWithTypeFor(id utils.ObjectId, gk schema.GroupKind) utils.ObjectIds { o.lock.RLock() defer o.lock.RUnlock() ids := utils.ObjectIds{} @@ -159,15 +149,15 @@ func (o *OwnerCache) GetSerfsWithTypeFor(id utils.ObjectId, gk schema.GroupKind) return ids } -func (o *OwnerCache) GetSerfsForObject(obj client.Object) utils.ObjectIds { +func (o *ownerCache) GetSerfsForObject(obj client.Object) utils.ObjectIds { return o.GetSerfsFor(utils.NewObjectId(obj)) } -func (o *OwnerCache) GetSerfsWithTypeForObject(obj client.Object, gk schema.GroupKind) utils.ObjectIds { +func (o *ownerCache) GetSerfsWithTypeForObject(obj client.Object, gk schema.GroupKind) utils.ObjectIds { return o.GetSerfsWithTypeFor(utils.NewObjectId(obj), gk) } -func (o *OwnerCache) CreateSerf(ctx context.Context, owner, serf client.Object, opts ...client.CreateOption) error { +func (o *ownerCache) CreateSerf(ctx context.Context, owner, serf client.Object, opts ...client.CreateOption) error { serf.SetOwnerReferences([]metav1.OwnerReference{OwnerRefForObject(owner)}) o.lock.Lock() defer o.lock.Unlock() diff --git a/pkg/manager/ownercachereconciler.go b/pkg/ownercache/ownercachereconciler.go similarity index 97% rename from pkg/manager/ownercachereconciler.go rename to pkg/ownercache/ownercachereconciler.go index 6f626d9b6..45a9b3b1d 100644 --- a/pkg/manager/ownercachereconciler.go +++ b/pkg/ownercache/ownercachereconciler.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package manager +package ownercache import ( "context" @@ -34,7 +34,7 @@ type ownerReconciler struct { client client.Client gk schema.GroupKind Log logr.Logger - cache *OwnerCache + cache *ownerCache setupOnce sync.Once objType reflect.Type } @@ -62,7 +62,7 @@ func (r *ownerReconciler) setup(ctx context.Context) { } } -func (r *ownerReconciler) SetupWithCache(ctx context.Context, cache *OwnerCache) error { +func (r *ownerReconciler) SetupWithCache(ctx context.Context, cache *ownerCache) error { r.client = cache.client r.cache = cache obj := utils.GetObjectForGroupKind(r.client, r.gk) diff --git a/pkg/ownercache/suite_test.go b/pkg/ownercache/suite_test.go new file mode 100644 index 000000000..b536dd9dd --- /dev/null +++ b/pkg/ownercache/suite_test.go @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 by the OnMetal authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ownercache + +import ( + "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "testing" +) + +func Test(t *testing.T) { + RegisterFailHandler(ginkgo.Fail) + ginkgo.RunSpecs(t, "Ownercache") +} diff --git a/pkg/trigger/interface.go b/pkg/trigger/interface.go new file mode 100644 index 000000000..3e710a158 --- /dev/null +++ b/pkg/trigger/interface.go @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 by the OnMetal authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package trigger + +import ( + "github.com/onmetal/onmetal-api/pkg/utils" + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/controller" +) + +// ReconcilationTrigger is used to register controllers +// to be externally triggerable for a dedicated object. +// given by its object id. +type ReconcilationTrigger interface { + // RegisterControllerFor registers a controller to receice + // reconcilation requests for a dedicated group kind, if + // the Trigger method is called for such an object id. + // + // ATTENTION: Because a controller is always responsible for ONLY + // one object type, but is does not provide a method to + // get this type, this is not validated during the registration. + // Therefore this method MUST only be called for a controller + // if it is handling this type of object. + RegisterControllerFor(gk schema.GroupKind, c controller.Controller) + + // Trigger triggers all controllers registered for the GroupKind of the given id + Trigger(id utils.ObjectId) +} diff --git a/pkg/manager/ready.go b/pkg/trigger/ready.go similarity index 98% rename from pkg/manager/ready.go rename to pkg/trigger/ready.go index 428edff0e..6bcceeb1e 100644 --- a/pkg/manager/ready.go +++ b/pkg/trigger/ready.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package manager +package trigger import "sync" diff --git a/pkg/trigger/suite_test.go b/pkg/trigger/suite_test.go new file mode 100644 index 000000000..72145b754 --- /dev/null +++ b/pkg/trigger/suite_test.go @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 by the OnMetal authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package trigger + +import ( + "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "testing" +) + +func Test(t *testing.T) { + RegisterFailHandler(ginkgo.Fail) + ginkgo.RunSpecs(t, "Trigger") +} diff --git a/pkg/manager/trigger.go b/pkg/trigger/trigger.go similarity index 71% rename from pkg/manager/trigger.go rename to pkg/trigger/trigger.go index da817570f..e1228a379 100644 --- a/pkg/manager/trigger.go +++ b/pkg/trigger/trigger.go @@ -1,4 +1,20 @@ -package manager +/* + * Copyright (c) 2021 by the OnMetal authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package trigger import ( "context" @@ -15,25 +31,6 @@ import ( "github.com/onmetal/onmetal-api/pkg/utils" ) -// ReconcilationTrigger is used to register controllers -// to be externally triggerable for a dedicated object. -// given by its object id. -type ReconcilationTrigger interface { - // RegisterControllerFor registers a controller to receice - // reconcilation requests for a dedicated group kind, if - // the Trigger method is called for such an object id. - // - // ATTENTION: Because a controller is always responsible for ONLY - // one object type, but is does not provide a method to - // get this type, this is not validated during the registration. - // Therefore this method MUST only be called for a controller - // if it is handling this type of object. - RegisterControllerFor(gk schema.GroupKind, c controller.Controller) - - // Trigger triggers all controllers registered for the GroupKind of the given id - Trigger(id utils.ObjectId) -} - type triggersources map[schema.GroupKind]*triggerSource // triggerSource is a fake source to catch watch start calls from controllers diff --git a/pkg/usagecache/usagecache.go b/pkg/usagecache/usagecache.go index f7d5a5a7f..b6152c7ad 100644 --- a/pkg/usagecache/usagecache.go +++ b/pkg/usagecache/usagecache.go @@ -19,7 +19,7 @@ package usagecache import ( "context" "fmt" - manager2 "github.com/onmetal/onmetal-api/pkg/manager" + "github.com/onmetal/onmetal-api/pkg/trigger" "sync" "k8s.io/apimachinery/pkg/runtime/schema" @@ -38,7 +38,7 @@ type usageCache struct { lock sync.RWMutex targets map[utils.ObjectId]ObjectUsageInfo sources map[utils.ObjectId]ObjectUsageInfo - ready manager2.Ready + ready trigger.Ready } type usageGKInfo struct { diff --git a/pkg/utils/suite_test.go b/pkg/utils/suite_test.go new file mode 100644 index 000000000..94a05f511 --- /dev/null +++ b/pkg/utils/suite_test.go @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 by the OnMetal authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package utils + +import ( + "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "testing" +) + +func Test(t *testing.T) { + RegisterFailHandler(ginkgo.Fail) + ginkgo.RunSpecs(t, "Utils") +}