@@ -2,12 +2,12 @@ package atlasdeployment
22
33import (
44 "context"
5+ "errors"
56 "fmt"
67 "net/http"
78 "testing"
89
9- internal "github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/searchindex"
10-
10+ "github.com/stretchr/testify/assert"
1111 "github.com/stretchr/testify/mock"
1212 "go.mongodb.org/atlas-sdk/v20231115008/admin"
1313 "go.mongodb.org/atlas-sdk/v20231115008/mockadmin"
@@ -18,14 +18,11 @@ import (
1818 "sigs.k8s.io/controller-runtime/pkg/client/fake"
1919
2020 "github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/pointer"
21+ internal "github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/searchindex"
22+ akov2 "github.com/mongodb/mongodb-atlas-kubernetes/v2/pkg/api/v1"
2123 "github.com/mongodb/mongodb-atlas-kubernetes/v2/pkg/api/v1/common"
22- "github.com/mongodb/mongodb-atlas-kubernetes/v2/pkg/controller/workflow"
23-
2424 "github.com/mongodb/mongodb-atlas-kubernetes/v2/pkg/api/v1/status"
25-
26- "github.com/stretchr/testify/assert"
27-
28- akov2 "github.com/mongodb/mongodb-atlas-kubernetes/v2/pkg/api/v1"
25+ "github.com/mongodb/mongodb-atlas-kubernetes/v2/pkg/controller/workflow"
2926)
3027
3128func Test_verifyAllIndexesNamesAreUnique (t * testing.T ) {
@@ -316,6 +313,182 @@ func Test_SearchIndexesReconcile(t *testing.T) {
316313 assert .False (t , result .IsOk ())
317314 })
318315
316+ t .Run ("Should proceed with index Type Search if it cannot be found: CREATE INDEX" , func (t * testing.T ) {
317+ mockSearchAPI := mockadmin .NewAtlasSearchApi (t )
318+
319+ nestMock := func (f func (nestedMock * mockadmin.AtlasSearchApi )) {
320+ nested := mockadmin .NewAtlasSearchApi (t )
321+ f (nested )
322+ }
323+
324+ searchIndexConfig := & akov2.AtlasSearchIndexConfig {
325+ TypeMeta : metav1.TypeMeta {},
326+ ObjectMeta : metav1.ObjectMeta {
327+ Name : "testConfig" ,
328+ Namespace : "testNamespace" ,
329+ },
330+ Spec : akov2.AtlasSearchIndexConfigSpec {
331+ Analyzer : pointer .MakePtr ("testAnalyzer" ),
332+ },
333+ Status : status.AtlasSearchIndexConfigStatus {},
334+ }
335+
336+ deployment := & akov2.AtlasDeployment {
337+ TypeMeta : metav1.TypeMeta {},
338+ ObjectMeta : metav1.ObjectMeta {},
339+ Spec : akov2.AtlasDeploymentSpec {
340+ DeploymentSpec : & akov2.AdvancedDeploymentSpec {
341+ Name : "testDeployment" ,
342+ SearchIndexes : []akov2.SearchIndex {
343+ {
344+ Name : "Index1" ,
345+ Type : IndexTypeSearch ,
346+ Search : & akov2.Search {
347+ SearchConfigurationRef : common.ResourceRefNamespaced {
348+ Name : searchIndexConfig .Name ,
349+ Namespace : searchIndexConfig .Namespace ,
350+ },
351+ },
352+ },
353+ },
354+ },
355+ },
356+ Status : status.AtlasDeploymentStatus {},
357+ }
358+
359+ sch := runtime .NewScheme ()
360+ assert .Nil (t , akov2 .AddToScheme (sch ))
361+ assert .Nil (t , corev1 .AddToScheme (sch ))
362+
363+ k8sClient := fake .NewClientBuilder ().
364+ WithScheme (sch ).
365+ WithObjects (deployment , searchIndexConfig ).
366+ Build ()
367+
368+ reconciler := searchIndexesReconciler {
369+ ctx : & workflow.Context {
370+ Log : zap .S (),
371+ OrgID : "testOrgID" ,
372+ Client : nil ,
373+ SdkClient : & admin.APIClient {AtlasSearchApi : mockSearchAPI },
374+ Context : context .Background (),
375+ },
376+ deployment : deployment ,
377+ k8sClient : k8sClient ,
378+ projectID : "testProjectID" ,
379+ }
380+
381+ // mock GET for Index1
382+ nestMock (func (n * mockadmin.AtlasSearchApi ) {
383+ mockSearchAPI .EXPECT ().
384+ GetAtlasSearchIndex (context .Background (), mock .Anything , mock .Anything , "123" ).
385+ Return (admin.GetAtlasSearchIndexApiRequest {ApiService : n })
386+ n .EXPECT ().
387+ GetAtlasSearchIndexExecute (admin.GetAtlasSearchIndexApiRequest {ApiService : n }).
388+ Return (
389+ & admin.ClusterSearchIndex {
390+ IndexID : pointer .MakePtr ("123" ),
391+ Name : "Index1" ,
392+ Status : pointer .MakePtr (IndexStatusActive ),
393+ Type : pointer .MakePtr (IndexTypeSearch ),
394+ Analyzer : pointer .MakePtr ("testAnalyzer" ),
395+ },
396+ & http.Response {StatusCode : http .StatusOK }, nil ,
397+ )
398+ })
399+
400+ // mock CREATE for Index1
401+ atlasIdx , err := internal .NewSearchIndexFromAKO (& deployment .Spec .DeploymentSpec .SearchIndexes [0 ], & searchIndexConfig .Spec ).ToAtlas ()
402+ assert .NoError (t , err )
403+
404+ nestMock (func (n * mockadmin.AtlasSearchApi ) {
405+ mockSearchAPI .EXPECT ().
406+ CreateAtlasSearchIndex (context .Background (), mock .Anything , mock .Anything , atlasIdx ).
407+ Return (admin.CreateAtlasSearchIndexApiRequest {ApiService : n })
408+ n .EXPECT ().
409+ CreateAtlasSearchIndexExecute (admin.CreateAtlasSearchIndexApiRequest {ApiService : n }).
410+ Return (
411+ & admin.ClusterSearchIndex {Name : "Index1" , IndexID : pointer .MakePtr ("123" ), Status : pointer .MakePtr ("NOT STARTED" )},
412+ & http.Response {StatusCode : http .StatusCreated }, nil ,
413+ )
414+ })
415+
416+ // first reconcile succeeds, creation succeeds
417+ result := reconciler .Reconcile ()
418+ deployment .UpdateStatus (reconciler .ctx .Conditions (), reconciler .ctx .StatusOptions ()... )
419+ assert .False (t , result .IsOk ())
420+ assert .Equal (t , []status.DeploymentSearchIndexStatus {
421+ {
422+ Name : "Index1" ,
423+ ID : "123" ,
424+ Status : "InProgress" ,
425+ Message : "Atlas search index status: NOT STARTED" ,
426+ },
427+ }, deployment .Status .SearchIndexes )
428+
429+ // Add another search Index2 which is a copy of Index1
430+ deployment .Spec .DeploymentSpec .SearchIndexes = append (deployment .Spec .DeploymentSpec .SearchIndexes , akov2.SearchIndex {
431+ Name : "Index2" ,
432+ Type : IndexTypeSearch ,
433+ Search : & akov2.Search {
434+ SearchConfigurationRef : common.ResourceRefNamespaced {
435+ Name : searchIndexConfig .Name ,
436+ Namespace : searchIndexConfig .Namespace ,
437+ },
438+ },
439+ })
440+
441+ // mock CREATE for Index2
442+ atlasIdx , err = internal .NewSearchIndexFromAKO (& deployment .Spec .DeploymentSpec .SearchIndexes [1 ], & searchIndexConfig .Spec ).ToAtlas ()
443+ assert .NoError (t , err )
444+
445+ nestMock (func (n * mockadmin.AtlasSearchApi ) {
446+ mockSearchAPI .EXPECT ().CreateAtlasSearchIndex (context .Background (), mock .Anything , mock .Anything , atlasIdx ).
447+ Return (admin.CreateAtlasSearchIndexApiRequest {ApiService : n })
448+ n .EXPECT ().
449+ CreateAtlasSearchIndexExecute (admin.CreateAtlasSearchIndexApiRequest {ApiService : n }).
450+ Return (
451+ nil ,
452+ & http.Response {StatusCode : http .StatusBadRequest }, errors .New ("conflict" ),
453+ )
454+ })
455+
456+ // create fails
457+ result = reconciler .Reconcile ()
458+ deployment .UpdateStatus (reconciler .ctx .Conditions (), reconciler .ctx .StatusOptions ()... )
459+ assert .False (t , result .IsOk ())
460+ assert .Equal (t , []status.DeploymentSearchIndexStatus {
461+ {
462+ Name : "Index1" ,
463+ ID : "123" ,
464+ Status : "Ready" ,
465+ Message : "Atlas search index status: STEADY" ,
466+ },
467+ {
468+ Name : "Index2" ,
469+ ID : "" ,
470+ Status : "Error" ,
471+ Message : "error with processing index Index2. err: failed to create index: conflict, status: 400" ,
472+ },
473+ }, deployment .Status .SearchIndexes )
474+
475+ // remove Index2 from the spec
476+ deployment .Spec .DeploymentSpec .SearchIndexes = []akov2.SearchIndex {deployment .Spec .DeploymentSpec .SearchIndexes [0 ]}
477+
478+ // third reconcile succeeds, creation succeeds
479+ result = reconciler .Reconcile ()
480+ deployment .UpdateStatus (reconciler .ctx .Conditions (), reconciler .ctx .StatusOptions ()... )
481+ assert .True (t , result .IsOk ())
482+ assert .Equal (t , []status.DeploymentSearchIndexStatus {
483+ {
484+ Name : "Index1" ,
485+ ID : "123" ,
486+ Status : "Ready" ,
487+ Message : "Atlas search index status: STEADY" ,
488+ },
489+ }, deployment .Status .SearchIndexes )
490+ })
491+
319492 t .Run ("Should proceed with the index Type Search: UPDATE INDEX" , func (t * testing.T ) {
320493 mockSearchAPI := mockadmin .NewAtlasSearchApi (t )
321494
0 commit comments