Skip to content

Commit ad40d6f

Browse files
authored
CLOUDP-279474: Use generator to avoid sharing list value (#1871)
* Add data race detection unit test * CLOUDP-279474: Use generator to avoid sharing list value
1 parent 756aba9 commit ad40d6f

File tree

4 files changed

+101
-38
lines changed

4 files changed

+101
-38
lines changed

pkg/controller/atlasdatabaseuser/atlasdatabaseuser_controller.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -222,13 +222,7 @@ func (r *AtlasDatabaseUserReconciler) SetupWithManager(mgr ctrl.Manager, skipNam
222222
).
223223
Watches(
224224
&corev1.Secret{},
225-
handler.EnqueueRequestsFromMapFunc(indexer.CredentialsIndexMapperFunc(
226-
indexer.AtlasDatabaseUserCredentialsIndex,
227-
&akov2.AtlasDatabaseUserList{},
228-
indexer.DatabaseUserRequests,
229-
r.Client,
230-
r.Log,
231-
)),
225+
handler.EnqueueRequestsFromMapFunc(r.databaseUsersForCredentialMapFunc()),
232226
builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}),
233227
).
234228
WithOptions(controller.TypedOptions[reconcile.Request]{SkipNameValidation: pointer.MakePtr(skipNameValidation)}).
@@ -272,6 +266,16 @@ func (r *AtlasDatabaseUserReconciler) findAtlasDatabaseUserForSecret(ctx context
272266
return requests
273267
}
274268

269+
func (r *AtlasDatabaseUserReconciler) databaseUsersForCredentialMapFunc() handler.MapFunc {
270+
return indexer.CredentialsIndexMapperFunc(
271+
indexer.AtlasDatabaseUserCredentialsIndex,
272+
func() *akov2.AtlasDatabaseUserList { return &akov2.AtlasDatabaseUserList{} },
273+
indexer.DatabaseUserRequests,
274+
r.Client,
275+
r.Log,
276+
)
277+
}
278+
275279
func NewAtlasDatabaseUserReconciler(
276280
mgr manager.Manager,
277281
predicates []predicate.Predicate,

pkg/controller/atlasdeployment/atlasdeployment_controller.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -451,13 +451,7 @@ func (r *AtlasDeploymentReconciler) SetupWithManager(mgr ctrl.Manager, skipNameV
451451
).
452452
Watches(
453453
&corev1.Secret{},
454-
handler.EnqueueRequestsFromMapFunc(indexer.CredentialsIndexMapperFunc(
455-
indexer.AtlasDeploymentCredentialsIndex,
456-
&akov2.AtlasDeploymentList{},
457-
indexer.DeploymentRequests,
458-
r.Client,
459-
r.Log,
460-
)),
454+
handler.EnqueueRequestsFromMapFunc(r.deploymentsForCredentialMapFunc()),
461455
builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}),
462456
).
463457
WithOptions(controller.TypedOptions[reconcile.Request]{SkipNameValidation: pointer.MakePtr(skipNameValidation)}).
@@ -595,3 +589,13 @@ func (r *AtlasDeploymentReconciler) findDeploymentsForSearchIndexConfig(ctx cont
595589

596590
return requests
597591
}
592+
593+
func (r *AtlasDeploymentReconciler) deploymentsForCredentialMapFunc() handler.MapFunc {
594+
return indexer.CredentialsIndexMapperFunc(
595+
indexer.AtlasDeploymentCredentialsIndex,
596+
func() *akov2.AtlasDeploymentList { return &akov2.AtlasDeploymentList{} },
597+
indexer.DeploymentRequests,
598+
r.Client,
599+
r.Log,
600+
)
601+
}

pkg/indexer/localcredentials.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func (lc *LocalCredentialIndexer) Keys(object client.Object) []string {
5757

5858
type requestsFunc[L client.ObjectList] func(L) []reconcile.Request
5959

60-
func CredentialsIndexMapperFunc[L client.ObjectList](indexerName string, list L, reqsFn requestsFunc[L], kubeClient client.Client, logger *zap.SugaredLogger) handler.MapFunc {
60+
func CredentialsIndexMapperFunc[L client.ObjectList](indexerName string, listGenFn func() L, reqsFn requestsFunc[L], kubeClient client.Client, logger *zap.SugaredLogger) handler.MapFunc {
6161
return func(ctx context.Context, obj client.Object) []reconcile.Request {
6262
secret, ok := obj.(*corev1.Secret)
6363
if !ok {
@@ -71,6 +71,7 @@ func CredentialsIndexMapperFunc[L client.ObjectList](indexerName string, list L,
7171
client.ObjectKeyFromObject(secret).String(),
7272
),
7373
}
74+
list := listGenFn()
7475
err := kubeClient.List(ctx, list, listOpts)
7576
if err != nil {
7677
logger.Errorf("failed to list from indexer %s: %v", indexerName, err)

pkg/indexer/localcredentials_test.go

Lines changed: 77 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package indexer
22

33
import (
44
"context"
5+
"fmt"
56
"sort"
7+
"sync"
68
"testing"
79

810
"github.com/stretchr/testify/assert"
@@ -21,6 +23,10 @@ import (
2123
akov2 "github.com/mongodb/mongodb-atlas-kubernetes/v2/pkg/api/v1"
2224
)
2325

26+
const (
27+
testUsername = "matching-user"
28+
)
29+
2430
func TestAtlasDatabaseUserLocalCredentialsIndexer(t *testing.T) {
2531
for _, tc := range []struct {
2632
name string
@@ -106,27 +112,8 @@ func TestCredentialsIndexMapperFunc(t *testing.T) {
106112
{
107113
name: "matching input credentials renders matching user",
108114
mapperFn: dbUserMapperFunc,
109-
input: &corev1.Secret{
110-
ObjectMeta: metav1.ObjectMeta{
111-
Name: "secret-ref",
112-
Namespace: "ns",
113-
},
114-
},
115-
objects: []client.Object{
116-
&akov2.AtlasDatabaseUser{
117-
ObjectMeta: metav1.ObjectMeta{
118-
Name: "matching-user",
119-
Namespace: "ns",
120-
},
121-
Spec: akov2.AtlasDatabaseUserSpec{
122-
LocalCredentialHolder: api.LocalCredentialHolder{
123-
ConnectionSecret: &api.LocalObjectReference{
124-
Name: "secret-ref",
125-
},
126-
},
127-
},
128-
},
129-
},
115+
input: newTestSecret("matching-user-secret-ref"),
116+
objects: []client.Object{newTestUser("matching-user")},
130117
want: []reconcile.Request{
131118
{NamespacedName: types.NamespacedName{
132119
Name: "matching-user",
@@ -161,10 +148,77 @@ func TestCredentialsIndexMapperFunc(t *testing.T) {
161148
}
162149
}
163150

151+
func TestCredentialsIndexMapperFuncRace(t *testing.T) {
152+
scheme := runtime.NewScheme()
153+
assert.NoError(t, corev1.AddToScheme(scheme))
154+
assert.NoError(t, akov2.AddToScheme(scheme))
155+
indexer := NewLocalCredentialsIndexer(
156+
AtlasDatabaseUserCredentialsIndex,
157+
&akov2.AtlasDatabaseUser{},
158+
zaptest.NewLogger(t),
159+
)
160+
objs := make([]client.Object, 10)
161+
for i := range objs {
162+
objs[i] = newTestUser(fmt.Sprintf("%s-%d", testUsername, i))
163+
}
164+
fakeClient := fake.NewClientBuilder().
165+
WithScheme(scheme).
166+
WithObjects(objs...).
167+
WithIndex(
168+
&akov2.AtlasDatabaseUser{},
169+
AtlasDatabaseUserCredentialsIndex,
170+
func(obj client.Object) []string {
171+
return indexer.Keys(obj)
172+
}).
173+
Build()
174+
fn := dbUserMapperFunc(fakeClient, zaptest.NewLogger(t).Sugar())
175+
ctx := context.Background()
176+
var wg sync.WaitGroup
177+
for i := 0; i < 100; i++ {
178+
wg.Add(1)
179+
go func(i int) {
180+
defer wg.Done()
181+
input := newTestSecret(fmt.Sprintf("%s-%d-secret-ref", testUsername, i))
182+
result := fn(ctx, input)
183+
if i < len(objs) {
184+
assert.NotEmpty(t, result, "failed to find for index %d", i)
185+
} else {
186+
assert.Empty(t, result, "failed not to find for index %d", i)
187+
}
188+
}(i)
189+
}
190+
wg.Wait()
191+
}
192+
193+
func newTestUser(username string) *akov2.AtlasDatabaseUser {
194+
return &akov2.AtlasDatabaseUser{
195+
ObjectMeta: metav1.ObjectMeta{
196+
Name: username,
197+
Namespace: "ns",
198+
},
199+
Spec: akov2.AtlasDatabaseUserSpec{
200+
LocalCredentialHolder: api.LocalCredentialHolder{
201+
ConnectionSecret: &api.LocalObjectReference{
202+
Name: fmt.Sprintf("%s-secret-ref", username),
203+
},
204+
},
205+
},
206+
}
207+
}
208+
209+
func newTestSecret(name string) *corev1.Secret {
210+
return &corev1.Secret{
211+
ObjectMeta: metav1.ObjectMeta{
212+
Name: name,
213+
Namespace: "ns",
214+
},
215+
}
216+
}
217+
164218
func dbUserMapperFunc(kubeClient client.Client, logger *zap.SugaredLogger) handler.MapFunc {
165-
return CredentialsIndexMapperFunc(
219+
return CredentialsIndexMapperFunc[*akov2.AtlasDatabaseUserList](
166220
AtlasDatabaseUserCredentialsIndex,
167-
&akov2.AtlasDatabaseUserList{},
221+
func() *akov2.AtlasDatabaseUserList { return &akov2.AtlasDatabaseUserList{} },
168222
DatabaseUserRequests,
169223
kubeClient,
170224
logger,

0 commit comments

Comments
 (0)