Skip to content

Commit

Permalink
add watching for projects in data federation
Browse files Browse the repository at this point in the history
  • Loading branch information
s-urbaniak committed Oct 15, 2024
1 parent 47827f8 commit 059e602
Show file tree
Hide file tree
Showing 5 changed files with 217 additions and 0 deletions.
36 changes: 36 additions & 0 deletions pkg/controller/atlasdatafederation/datafederation_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ import (

"go.uber.org/zap"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/record"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
Expand All @@ -27,6 +30,7 @@ import (
"github.com/mongodb/mongodb-atlas-kubernetes/v2/pkg/controller/customresource"
"github.com/mongodb/mongodb-atlas-kubernetes/v2/pkg/controller/statushandler"
"github.com/mongodb/mongodb-atlas-kubernetes/v2/pkg/controller/workflow"
"github.com/mongodb/mongodb-atlas-kubernetes/v2/pkg/indexer"
)

// AtlasDataFederationReconciler reconciles an DataFederation object
Expand Down Expand Up @@ -211,10 +215,42 @@ func (r *AtlasDataFederationReconciler) SetupWithManager(mgr ctrl.Manager, skipN
return ctrl.NewControllerManagedBy(mgr).
Named("AtlasDataFederation").
For(&akov2.AtlasDataFederation{}, builder.WithPredicates(r.GlobalPredicates...)).
Watches(
&akov2.AtlasProject{},
handler.EnqueueRequestsFromMapFunc(r.findAtlasDataFederationForProjects),
builder.WithPredicates(predicate.GenerationChangedPredicate{}),
).
WithOptions(controller.TypedOptions[reconcile.Request]{SkipNameValidation: pointer.MakePtr(skipNameValidation)}).
Complete(r)
}

func (r *AtlasDataFederationReconciler) findAtlasDataFederationForProjects(ctx context.Context, obj client.Object) []reconcile.Request {
project, ok := obj.(*akov2.AtlasProject)
if !ok {
r.Log.Warnf("watching AtlasProject but got %T", obj)
return nil
}

datafederationList := &akov2.AtlasDataFederationList{}
listOps := &client.ListOptions{
FieldSelector: fields.OneTermEqualSelector(
indexer.AtlasDataFederationByProject,
client.ObjectKeyFromObject(project).String(),
),
}
err := r.Client.List(ctx, datafederationList, listOps)
if err != nil {
r.Log.Errorf("failed to list AtlasDataFederation: %e", err)
return []reconcile.Request{}
}

requests := make([]reconcile.Request, 0, len(datafederationList.Items))
for _, item := range datafederationList.Items {
requests = append(requests, reconcile.Request{NamespacedName: types.NamespacedName{Name: item.Name, Namespace: item.Namespace}})
}
return requests
}

func NewAtlasDataFederationReconciler(
mgr manager.Manager,
predicates []predicate.Predicate,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package atlasdatafederation

import (
"context"
"reflect"
"testing"

"github.com/stretchr/testify/assert"
Expand All @@ -10,9 +11,11 @@ import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

"github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/mocks/translation"
"github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/translation/datafederation"
Expand All @@ -22,6 +25,7 @@ import (
"github.com/mongodb/mongodb-atlas-kubernetes/v2/pkg/controller/connectionsecret"
"github.com/mongodb/mongodb-atlas-kubernetes/v2/pkg/controller/customresource"
"github.com/mongodb/mongodb-atlas-kubernetes/v2/pkg/controller/workflow"
"github.com/mongodb/mongodb-atlas-kubernetes/v2/pkg/indexer"
)

func TestDeleteConnectionSecrets(t *testing.T) {
Expand Down Expand Up @@ -237,3 +241,69 @@ func TestDeleteConnectionSecrets(t *testing.T) {
})
}
}

func TestFindAtlasDataFederationForProjects(t *testing.T) {
for _, tc := range []struct {
name string
obj client.Object
initObjs []client.Object
want []reconcile.Request
}{
{
name: "wrong type",
obj: &akov2.AtlasDeployment{},
want: nil,
},
{
name: "same namespace",
obj: &akov2.AtlasProject{
ObjectMeta: metav1.ObjectMeta{Name: "project", Namespace: "ns"},
},
initObjs: []client.Object{
&akov2.AtlasDataFederation{
ObjectMeta: metav1.ObjectMeta{Name: "adf1", Namespace: "ns"},
Spec: akov2.DataFederationSpec{
Project: common.ResourceRefNamespaced{Name: "project"},
},
},
},
want: []reconcile.Request{
{NamespacedName: types.NamespacedName{Name: "adf1", Namespace: "ns"}},
},
},
{
name: "different namespace",
obj: &akov2.AtlasProject{
ObjectMeta: metav1.ObjectMeta{Name: "project", Namespace: "ns2"},
},
initObjs: []client.Object{
&akov2.AtlasDataFederation{
ObjectMeta: metav1.ObjectMeta{Name: "adf1", Namespace: "ns"},
Spec: akov2.DataFederationSpec{
Project: common.ResourceRefNamespaced{Name: "project"},
},
},
},
want: []reconcile.Request{},
},
} {
t.Run(tc.name, func(t *testing.T) {
testScheme := runtime.NewScheme()
assert.NoError(t, akov2.AddToScheme(testScheme))
idx := indexer.NewAtlasDataFederationByProjectIndexer(zaptest.NewLogger(t))
k8sClient := fake.NewClientBuilder().
WithScheme(testScheme).
WithObjects(tc.initObjs...).
WithIndex(idx.Object(), idx.Name(), idx.Keys).
Build()
reconciler := &AtlasDataFederationReconciler{
Log: zaptest.NewLogger(t).Sugar(),
Client: k8sClient,
}
got := reconciler.findAtlasDataFederationForProjects(context.Background(), tc.obj)
if !reflect.DeepEqual(got, tc.want) {
t.Errorf("want reconcile requests: %v, got %v", got, tc.want)
}
})
}
}
44 changes: 44 additions & 0 deletions pkg/indexer/atlasdatafederationprojects.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package indexer

import (
"go.uber.org/zap"
"sigs.k8s.io/controller-runtime/pkg/client"

akov2 "github.com/mongodb/mongodb-atlas-kubernetes/v2/pkg/api/v1"
)

const (
AtlasDataFederationByProject = "atlasdatafederation.spec.project"
)

type AtlasDataFederationByProjectIndexer struct {
logger *zap.SugaredLogger
}

func NewAtlasDataFederationByProjectIndexer(logger *zap.Logger) *AtlasDataFederationByProjectIndexer {
return &AtlasDataFederationByProjectIndexer{
logger: logger.Named(AtlasDatabaseUserByProject).Sugar(),
}
}

func (*AtlasDataFederationByProjectIndexer) Object() client.Object {
return &akov2.AtlasDataFederation{}
}

func (*AtlasDataFederationByProjectIndexer) Name() string {
return AtlasDataFederationByProject
}

func (a *AtlasDataFederationByProjectIndexer) Keys(object client.Object) []string {
datafederation, ok := object.(*akov2.AtlasDataFederation)
if !ok {
a.logger.Errorf("expected *AtlasDataFederation but got %T", object)
return nil
}

if datafederation.Spec.Project.IsEmpty() {
return nil
}

return []string{datafederation.Spec.Project.GetObject(datafederation.Namespace).String()}
}
66 changes: 66 additions & 0 deletions pkg/indexer/atlasdatafederationprojects_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package indexer

import (
"sort"
"testing"

"github.com/stretchr/testify/assert"
"go.uber.org/zap/zaptest"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"

akov2 "github.com/mongodb/mongodb-atlas-kubernetes/v2/pkg/api/v1"
"github.com/mongodb/mongodb-atlas-kubernetes/v2/pkg/api/v1/common"
)

func TestAtlasDataFederationByProjectIndexer(t *testing.T) {
for _, tc := range []struct {
name string
object client.Object
wantKeys []string
}{
{
name: "should return nil on wrong type",
object: &akov2.AtlasDeployment{},
},
{
name: "should return nil when there are no references",
object: &akov2.AtlasDataFederation{},
},
{
name: "should return nil when there is an empty reference",
object: &akov2.AtlasDataFederation{
Spec: akov2.DataFederationSpec{
Project: common.ResourceRefNamespaced{},
},
},
},
{
name: "should return project namespace if name is set only",
object: &akov2.AtlasDataFederation{
ObjectMeta: metav1.ObjectMeta{Name: "name", Namespace: "ns"},
Spec: akov2.DataFederationSpec{
Project: common.ResourceRefNamespaced{Name: "someProject"},
},
},
wantKeys: []string{"ns/someProject"},
},
{
name: "should return secret namespace and name if set",
object: &akov2.AtlasDataFederation{
ObjectMeta: metav1.ObjectMeta{Name: "name", Namespace: "ns"},
Spec: akov2.DataFederationSpec{
Project: common.ResourceRefNamespaced{Name: "someProject", Namespace: "otherNs"},
},
},
wantKeys: []string{"otherNs/someProject"},
},
} {
t.Run(tc.name, func(t *testing.T) {
indexer := NewAtlasDataFederationByProjectIndexer(zaptest.NewLogger(t))
keys := indexer.Keys(tc.object)
sort.Strings(keys)
assert.Equal(t, tc.wantKeys, keys)
})
}
}
1 change: 1 addition & 0 deletions pkg/indexer/indexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func RegisterAll(ctx context.Context, mgr manager.Manager, logger *zap.Logger) e
NewAtlasDatabaseUserByCredentialIndexer(logger),
NewAtlasDeploymentByCredentialIndexer(logger),
NewAtlasDatabaseUserByProjectIndexer(ctx, mgr.GetClient(), logger),
NewAtlasDataFederationByProjectIndexer(logger),
)
}

Expand Down

0 comments on commit 059e602

Please sign in to comment.