Skip to content

Commit

Permalink
Merge pull request #4 from projectsveltos/cluster-feature-transformat…
Browse files Browse the repository at this point in the history
…ions

ClusterFeature transformations
  • Loading branch information
gianlucam76 committed Jul 22, 2022
2 parents e247110 + 65518de commit 74c80bd
Show file tree
Hide file tree
Showing 8 changed files with 273 additions and 107 deletions.
2 changes: 1 addition & 1 deletion api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

79 changes: 77 additions & 2 deletions controllers/clusterfeature_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"context"
"fmt"
"reflect"
"sync"

"github.com/go-logr/logr"
"github.com/pkg/errors"
Expand All @@ -46,8 +47,36 @@ import (
// ClusterFeatureReconciler reconciles a ClusterFeature object
type ClusterFeatureReconciler struct {
client.Client
Scheme *runtime.Scheme
Log logr.Logger
Scheme *runtime.Scheme
Log logr.Logger
Mux sync.Mutex // use a Mutex to update Map as MaxConcurrentReconciles is higher than one
ClusterMap map[string]*Set // key: CAPI Cluster namespace/name; value: set of all ClusterFeatures matching the Cluster
ClusterFeatureMap map[string]*Set // key: ClusterFeature name; value: set of CAPI Clusters matched
ClusterFeatures map[string]configv1alpha1.Selector // key: ClusterFeature name; value ClusterFeature Selector

// Reason for the two maps:
// ClusterFeature, via ClusterSelector, matches CAPI Clusters based on Cluster labels.
// When a CAPI Cluster labels change, one or more ClusterFeature needs to be reconciled.
// In order to achieve so, ClusterFeature reconciler watches for CAPI Clusters. When a CAPI Cluster label changes,
// find all the ClusterFeatures currently referencing it and reconcile those.
// Problem is no I/O should be present inside a MapFunc (given a CAPI Cluster, return all the ClusterFeatures matching it).
// In the MapFunc, if the list ClusterFeatures operation failed, we would be unable to retry or re-enqueue the rigth set of
// ClusterFeatures.
// Instead the approach taken is following:
// - when a ClusterFeature is reconciled, update the ClusterFeatures amd the ClusterMap;
// - in the MapFunc, given the CAPI Cluster that changed:
// * use ClusterFeatures to find all ClusterFeature matching the Cluster and reconcile those;
// - in order to reconcile ClusterFeatures previously matching the Cluster and not anymore, use ClusterMap.
//
// The ClusterFeatureMap is used to update ClusterMap. Consider following scenarios to understand the need:
// 1. ClusterFeature A references Clusters 1 and 2. When reconciled, ClusterMap will have 1 => A and 2 => A;
// and ClusterFeatureMap A => 1,2
// 2. Cluster 2 label changes and now ClusterFeature matches Cluster 1 only. We ned to remove the entry 2 => A in ClusterMap. But
// when we reconcile ClusterFeature we have its current version we don't have its previous version. So we know ClusterFeature A
// now matches CAPI Cluster 1, but we don't know it used to match CAPI Cluster 2.
// So we use ClusterFeatureMap (at this point value stored here corresponds to reconciliation #1. We know currently
// ClusterFeature matches CAPI Cluster 1 only and looking at ClusterFeatureMap we know it used to reference CAPI Cluster 1 and 2.
// So we can remove 2 => A from ClusterMap. Only after this update, we update ClusterFeatureMap (so new value will be A => 1)
}

//+kubebuilder:rbac:groups=config.projectsveltos.io,resources=clusterfeatures,verbs=get;list;watch;update
Expand Down Expand Up @@ -142,6 +171,8 @@ func (r *ClusterFeatureReconciler) reconcileNormal(

clusterFeatureScope.SetMatchingClusters(matchingCluster)

r.updatesMaps(clusterFeatureScope)

// For each matching CAPI Cluster, create/update corresponding ClusterSummary
if err := r.updateClusterSummaries(ctx, clusterFeatureScope); err != nil {
return reconcile.Result{}, err
Expand Down Expand Up @@ -392,3 +423,47 @@ func (r *ClusterFeatureReconciler) getMachinesForCluster(

return &machineList, nil
}

func (r *ClusterFeatureReconciler) updatesMaps(clusterFeatureScope *scope.ClusterFeatureScope) {
currentClusters := Set{}
for i := range clusterFeatureScope.ClusterFeature.Status.MatchingClusters {
cluster := clusterFeatureScope.ClusterFeature.Status.MatchingClusters[i]
clusterName := cluster.Namespace + "/" + cluster.Name
currentClusters.insert(clusterName)
}

r.Mux.Lock()
defer r.Mux.Unlock()

// Get list of Clusters not matched anymore by ClusterFeature
var toBeRemoved []string
if v, ok := r.ClusterFeatureMap[clusterFeatureScope.Name()]; ok {
toBeRemoved = v.difference(currentClusters)
}

// For each currently matching Cluster, add ClusterFeature as consumer
for i := range clusterFeatureScope.ClusterFeature.Status.MatchingClusters {
cluster := clusterFeatureScope.ClusterFeature.Status.MatchingClusters[i]
clusterName := cluster.Namespace + "/" + cluster.Name
r.getClusterMapForEntry(clusterName).insert(clusterFeatureScope.Name())
}

// For each Cluster not matched anymore, remove ClusterFeature as consumer
for i := range toBeRemoved {
clusterName := toBeRemoved[i]
r.getClusterMapForEntry(clusterName).erase(clusterFeatureScope.Name())
}

// Update list of WorklaodRoles currently referenced by ClusterSummary
r.ClusterFeatureMap[clusterFeatureScope.Name()] = &currentClusters
r.ClusterFeatures[clusterFeatureScope.Name()] = clusterFeatureScope.ClusterFeature.Spec.ClusterSelector
}

func (r *ClusterFeatureReconciler) getClusterMapForEntry(entry string) *Set {
s := r.ClusterMap[entry]
if s == nil {
s = &Set{}
r.ClusterMap[entry] = s
}
return s
}
121 changes: 85 additions & 36 deletions controllers/clusterfeature_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package controllers_test
import (
"context"
"reflect"
"sync"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
Expand Down Expand Up @@ -99,9 +100,13 @@ var _ = Describe("ClusterFeature: Reconciler", func() {
c := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjects...).Build()

reconciler := &controllers.ClusterFeatureReconciler{
Client: c,
Log: klogr.New(),
Scheme: scheme,
Client: c,
Log: klogr.New(),
Scheme: scheme,
ClusterMap: make(map[string]*controllers.Set),
ClusterFeatureMap: make(map[string]*controllers.Set),
ClusterFeatures: make(map[string]configv1alpha1.Selector),
Mux: sync.Mutex{},
}

clusterFeatureName := client.ObjectKey{
Expand Down Expand Up @@ -133,9 +138,13 @@ var _ = Describe("ClusterFeature: Reconciler", func() {
c := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjects...).Build()

reconciler := &controllers.ClusterFeatureReconciler{
Client: c,
Log: klogr.New(),
Scheme: scheme,
Client: c,
Log: klogr.New(),
Scheme: scheme,
ClusterMap: make(map[string]*controllers.Set),
ClusterFeatureMap: make(map[string]*controllers.Set),
ClusterFeatures: make(map[string]configv1alpha1.Selector),
Mux: sync.Mutex{},
}

clusterFeatureScope, err := scope.NewClusterFeatureScope(scope.ClusterFeatureScopeParams{
Expand Down Expand Up @@ -171,9 +180,13 @@ var _ = Describe("ClusterFeature: Reconciler", func() {
Expect(err).To(BeNil())

reconciler := &controllers.ClusterFeatureReconciler{
Client: c,
Log: klogr.New(),
Scheme: scheme,
Client: c,
Log: klogr.New(),
Scheme: scheme,
ClusterMap: make(map[string]*controllers.Set),
ClusterFeatureMap: make(map[string]*controllers.Set),
ClusterFeatures: make(map[string]configv1alpha1.Selector),
Mux: sync.Mutex{},
}

err = controllers.CreateClusterSummary(reconciler, context.TODO(),
Expand Down Expand Up @@ -239,9 +252,13 @@ var _ = Describe("ClusterFeature: Reconciler", func() {
Expect(err).To(BeNil())

reconciler := &controllers.ClusterFeatureReconciler{
Client: c,
Log: klogr.New(),
Scheme: scheme,
Client: c,
Log: klogr.New(),
Scheme: scheme,
ClusterMap: make(map[string]*controllers.Set),
ClusterFeatureMap: make(map[string]*controllers.Set),
ClusterFeatures: make(map[string]configv1alpha1.Selector),
Mux: sync.Mutex{},
}

err = controllers.UpdateClusterSummary(reconciler, context.TODO(),
Expand Down Expand Up @@ -307,9 +324,13 @@ var _ = Describe("ClusterFeature: Reconciler", func() {
Expect(err).To(BeNil())

reconciler := &controllers.ClusterFeatureReconciler{
Client: c,
Log: klogr.New(),
Scheme: scheme,
Client: c,
Log: klogr.New(),
Scheme: scheme,
ClusterMap: make(map[string]*controllers.Set),
ClusterFeatureMap: make(map[string]*controllers.Set),
ClusterFeatures: make(map[string]configv1alpha1.Selector),
Mux: sync.Mutex{},
}

err = controllers.UpdateClusterSummary(reconciler, context.TODO(),
Expand Down Expand Up @@ -366,9 +387,13 @@ var _ = Describe("ClusterFeature: Reconciler", func() {
c := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjects...).Build()

reconciler := &controllers.ClusterFeatureReconciler{
Client: c,
Log: klogr.New(),
Scheme: scheme,
Client: c,
Log: klogr.New(),
Scheme: scheme,
ClusterMap: make(map[string]*controllers.Set),
ClusterFeatureMap: make(map[string]*controllers.Set),
ClusterFeatures: make(map[string]configv1alpha1.Selector),
Mux: sync.Mutex{},
}

err := controllers.DeleteClusterSummary(reconciler, context.TODO(), clusterSummary)
Expand All @@ -395,9 +420,13 @@ var _ = Describe("ClusterFeature: Reconciler", func() {
c := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjects...).Build()

reconciler := &controllers.ClusterFeatureReconciler{
Client: c,
Log: klogr.New(),
Scheme: scheme,
Client: c,
Log: klogr.New(),
Scheme: scheme,
ClusterMap: make(map[string]*controllers.Set),
ClusterFeatureMap: make(map[string]*controllers.Set),
ClusterFeatures: make(map[string]configv1alpha1.Selector),
Mux: sync.Mutex{},
}

clusterFeatureScope, err := scope.NewClusterFeatureScope(scope.ClusterFeatureScopeParams{
Expand Down Expand Up @@ -445,9 +474,13 @@ var _ = Describe("ClusterFeature: Reconciler", func() {
c := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjects...).Build()

reconciler := &controllers.ClusterFeatureReconciler{
Client: c,
Log: klogr.New(),
Scheme: scheme,
Client: c,
Log: klogr.New(),
Scheme: scheme,
ClusterMap: make(map[string]*controllers.Set),
ClusterFeatureMap: make(map[string]*controllers.Set),
ClusterFeatures: make(map[string]configv1alpha1.Selector),
Mux: sync.Mutex{},
}

clusterFeatureScope, err := scope.NewClusterFeatureScope(scope.ClusterFeatureScopeParams{
Expand Down Expand Up @@ -525,9 +558,13 @@ var _ = Describe("ClusterFeature: Reconciler", func() {
c := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjects...).Build()

reconciler := &controllers.ClusterFeatureReconciler{
Client: c,
Log: klogr.New(),
Scheme: scheme,
Client: c,
Log: klogr.New(),
Scheme: scheme,
ClusterMap: make(map[string]*controllers.Set),
ClusterFeatureMap: make(map[string]*controllers.Set),
ClusterFeatures: make(map[string]configv1alpha1.Selector),
Mux: sync.Mutex{},
}

clusterFeatureScope, err := scope.NewClusterFeatureScope(scope.ClusterFeatureScopeParams{
Expand Down Expand Up @@ -578,9 +615,13 @@ var _ = Describe("ClusterFeature: Reconciler", func() {
c := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjects...).Build()

reconciler := &controllers.ClusterFeatureReconciler{
Client: c,
Log: klogr.New(),
Scheme: scheme,
Client: c,
Log: klogr.New(),
Scheme: scheme,
ClusterMap: make(map[string]*controllers.Set),
ClusterFeatureMap: make(map[string]*controllers.Set),
ClusterFeatures: make(map[string]configv1alpha1.Selector),
Mux: sync.Mutex{},
}

clusterFeatureScope, err := scope.NewClusterFeatureScope(scope.ClusterFeatureScopeParams{
Expand Down Expand Up @@ -627,9 +668,13 @@ var _ = Describe("ClusterFeature: Reconciler", func() {
c := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjects...).Build()

reconciler := &controllers.ClusterFeatureReconciler{
Client: c,
Log: klogr.New(),
Scheme: scheme,
Client: c,
Log: klogr.New(),
Scheme: scheme,
ClusterMap: make(map[string]*controllers.Set),
ClusterFeatureMap: make(map[string]*controllers.Set),
ClusterFeatures: make(map[string]configv1alpha1.Selector),
Mux: sync.Mutex{},
}

clusterFeatureScope, err := scope.NewClusterFeatureScope(scope.ClusterFeatureScopeParams{
Expand Down Expand Up @@ -677,9 +722,13 @@ var _ = Describe("ClusterFeature: Reconciler", func() {
c := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjects...).Build()

reconciler := &controllers.ClusterFeatureReconciler{
Client: c,
Log: klogr.New(),
Scheme: scheme,
Client: c,
Log: klogr.New(),
Scheme: scheme,
ClusterMap: make(map[string]*controllers.Set),
ClusterFeatureMap: make(map[string]*controllers.Set),
ClusterFeatures: make(map[string]configv1alpha1.Selector),
Mux: sync.Mutex{},
}

clusterFeatureScope, err := scope.NewClusterFeatureScope(scope.ClusterFeatureScopeParams{
Expand Down
Loading

0 comments on commit 74c80bd

Please sign in to comment.