Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 0 additions & 129 deletions test/e2e/e2eutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,15 @@ import (
"context"
"fmt"
"reflect"
"testing"
"time"

"github.com/mongodb/mongodb-kubernetes-operator/pkg/util/envvar"

"github.com/pkg/errors"

mdbv1 "github.com/mongodb/mongodb-kubernetes-operator/api/v1"
"github.com/mongodb/mongodb-kubernetes-operator/pkg/kube/statefulset"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
apiErrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
k8sClient "sigs.k8s.io/controller-runtime/pkg/client"
)

Expand All @@ -47,127 +39,6 @@ func UpdateMongoDBResource(original *mdbv1.MongoDBCommunity, updateFunc func(*md
return TestClient.Update(context.TODO(), original)
}

// WaitForConfigMapToExist waits until a ConfigMap of the given name exists
// using the provided retryInterval and timeout
func WaitForConfigMapToExist(cmName string, retryInterval, timeout time.Duration) (corev1.ConfigMap, error) {
cm := corev1.ConfigMap{}
return cm, waitForRuntimeObjectToExist(cmName, retryInterval, timeout, &cm, OperatorNamespace)
}

// WaitForSecretToExist waits until a Secret of the given name exists
// using the provided retryInterval and timeout
func WaitForSecretToExist(cmName string, retryInterval, timeout time.Duration, namespace string) (corev1.Secret, error) {
s := corev1.Secret{}
return s, waitForRuntimeObjectToExist(cmName, retryInterval, timeout, &s, namespace)
}

// WaitForMongoDBToReachPhase waits until the given MongoDB resource reaches the expected phase
func WaitForMongoDBToReachPhase(t *testing.T, mdb *mdbv1.MongoDBCommunity, phase mdbv1.Phase, retryInterval, timeout time.Duration) error {
return waitForMongoDBCondition(mdb, retryInterval, timeout, func(db mdbv1.MongoDBCommunity) bool {
t.Logf("current phase: %s, waiting for phase: %s", db.Status.Phase, phase)
return db.Status.Phase == phase
})
}

// waitForMongoDBCondition polls and waits for a given condition to be true
func waitForMongoDBCondition(mdb *mdbv1.MongoDBCommunity, retryInterval, timeout time.Duration, condition func(mdbv1.MongoDBCommunity) bool) error {
mdbNew := mdbv1.MongoDBCommunity{}
return wait.Poll(retryInterval, timeout, func() (done bool, err error) {
err = TestClient.Get(context.TODO(), mdb.NamespacedName(), &mdbNew)
if err != nil {
return false, err
}
ready := condition(mdbNew)
return ready, nil
})
}

// WaitForStatefulSetToExist waits until a StatefulSet of the given name exists
// using the provided retryInterval and timeout
func WaitForStatefulSetToExist(stsName string, retryInterval, timeout time.Duration, namespace string) (appsv1.StatefulSet, error) {
sts := appsv1.StatefulSet{}
return sts, waitForRuntimeObjectToExist(stsName, retryInterval, timeout, &sts, namespace)
}

// WaitForStatefulSetToHaveUpdateStrategy waits until all replicas of the StatefulSet with the given name
// have reached the ready status
func WaitForStatefulSetToHaveUpdateStrategy(t *testing.T, mdb *mdbv1.MongoDBCommunity, strategy appsv1.StatefulSetUpdateStrategyType, retryInterval, timeout time.Duration) error {
return waitForStatefulSetCondition(t, mdb, retryInterval, timeout, func(sts appsv1.StatefulSet) bool {
return sts.Spec.UpdateStrategy.Type == strategy
})
}

// WaitForStatefulSetToBeReady waits until all replicas of the StatefulSet with the given name
// have reached the ready status
func WaitForStatefulSetToBeReady(t *testing.T, mdb *mdbv1.MongoDBCommunity, retryInterval, timeout time.Duration) error {
return waitForStatefulSetCondition(t, mdb, retryInterval, timeout, func(sts appsv1.StatefulSet) bool {
return statefulset.IsReady(sts, mdb.Spec.Members)
})
}

// waitForStatefulSetCondition waits until all replicas of the StatefulSet with the given name
// is not ready.
func WaitForStatefulSetToBeUnready(t *testing.T, mdb *mdbv1.MongoDBCommunity, retryInterval, timeout time.Duration) error {
return waitForStatefulSetCondition(t, mdb, retryInterval, timeout, func(sts appsv1.StatefulSet) bool {
return !statefulset.IsReady(sts, mdb.Spec.Members)
})
}

// WaitForStatefulSetToBeReadyAfterScaleDown waits for just the ready replicas to be correct
// and does not account for the updated replicas
func WaitForStatefulSetToBeReadyAfterScaleDown(t *testing.T, mdb *mdbv1.MongoDBCommunity, retryInterval, timeout time.Duration) error {
return waitForStatefulSetCondition(t, mdb, retryInterval, timeout, func(sts appsv1.StatefulSet) bool {
return int32(mdb.Spec.Members) == sts.Status.ReadyReplicas
})
}

func waitForStatefulSetCondition(t *testing.T, mdb *mdbv1.MongoDBCommunity, retryInterval, timeout time.Duration, condition func(set appsv1.StatefulSet) bool) error {
_, err := WaitForStatefulSetToExist(mdb.Name, retryInterval, timeout, mdb.Namespace)
if err != nil {
return errors.Errorf("error waiting for stateful set to be created: %s", err)
}

sts := appsv1.StatefulSet{}
return wait.Poll(retryInterval, timeout, func() (done bool, err error) {
err = TestClient.Get(context.TODO(), mdb.NamespacedName(), &sts)
if err != nil {
return false, err
}
t.Logf("Waiting for %s to have %d replicas. Current ready replicas: %d, Current updated replicas: %d, Current generation: %d, Observed Generation: %d\n",
mdb.Name, mdb.Spec.Members, sts.Status.ReadyReplicas, sts.Status.UpdatedReplicas, sts.Generation, sts.Status.ObservedGeneration)
ready := condition(sts)
return ready, nil
})
}

func WaitForPodReadiness(t *testing.T, isReady bool, containerName string, timeout time.Duration, pod corev1.Pod) error {
return wait.Poll(time.Second*3, timeout, func() (done bool, err error) {
err = TestClient.Get(context.TODO(), types.NamespacedName{Name: pod.Name, Namespace: pod.Namespace}, &pod)
if err != nil {
return false, err
}
for _, status := range pod.Status.ContainerStatuses {
t.Logf("%s (%s), ready: %v\n", pod.Name, status.Name, status.Ready)
if status.Name == containerName && status.Ready == isReady {
return true, nil
}
}
return false, nil
})
}

// waitForRuntimeObjectToExist waits until a runtime.Object of the given name exists
// using the provided retryInterval and timeout provided.
func waitForRuntimeObjectToExist(name string, retryInterval, timeout time.Duration, obj client.Object, namespace string) error {
return wait.Poll(retryInterval, timeout, func() (done bool, err error) {
err = TestClient.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: namespace}, obj)
if err != nil {
return false, client.IgnoreNotFound(err)
}
return true, nil
})
}

func NewTestMongoDB(ctx *Context, name string, namespace string) (mdbv1.MongoDBCommunity, mdbv1.MongoDBUser) {
mongodbNamespace := namespace
if mongodbNamespace == "" {
Expand Down
24 changes: 13 additions & 11 deletions test/e2e/mongodbtests/mongodbtests.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/mongodb/mongodb-kubernetes-operator/pkg/kube/container"
"testing"
"time"

"github.com/mongodb/mongodb-kubernetes-operator/pkg/kube/container"
"github.com/mongodb/mongodb-kubernetes-operator/test/e2e/util/wait"

mdbv1 "github.com/mongodb/mongodb-kubernetes-operator/api/v1"
"github.com/mongodb/mongodb-kubernetes-operator/pkg/authentication/scram"
"github.com/mongodb/mongodb-kubernetes-operator/pkg/automationconfig"
Expand Down Expand Up @@ -46,7 +48,7 @@ func StatefulSetBecomesUnready(mdb *mdbv1.MongoDBCommunity) func(t *testing.T) {
// failure threshold being high
func StatefulSetIsReadyAfterScaleDown(mdb *mdbv1.MongoDBCommunity) func(t *testing.T) {
return func(t *testing.T) {
err := e2eutil.WaitForStatefulSetToBeReadyAfterScaleDown(t, mdb, time.Second*60, time.Minute*45)
err := wait.ForStatefulSetToBeReadyAfterScaleDown(t, mdb, time.Second*60, time.Minute*45)
if err != nil {
t.Fatal(err)
}
Expand All @@ -58,7 +60,7 @@ func StatefulSetIsReadyAfterScaleDown(mdb *mdbv1.MongoDBCommunity) func(t *testi
// reaches the running state
func statefulSetIsReady(mdb *mdbv1.MongoDBCommunity, interval time.Duration, timeout time.Duration) func(t *testing.T) {
return func(t *testing.T) {
err := e2eutil.WaitForStatefulSetToBeReady(t, mdb, interval, timeout)
err := wait.ForStatefulSetToBeReady(t, mdb, interval, timeout)
if err != nil {
t.Fatal(err)
}
Expand All @@ -69,7 +71,7 @@ func statefulSetIsReady(mdb *mdbv1.MongoDBCommunity, interval time.Duration, tim
// statefulSetIsNotReady ensures that the underlying stateful set reaches the unready state.
func statefulSetIsNotReady(mdb *mdbv1.MongoDBCommunity, interval time.Duration, timeout time.Duration) func(t *testing.T) {
return func(t *testing.T) {
err := e2eutil.WaitForStatefulSetToBeUnready(t, mdb, interval, timeout)
err := wait.ForStatefulSetToBeUnready(t, mdb, interval, timeout)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -136,7 +138,7 @@ func ConnectionStringSecretsAreConfigured(mdb *mdbv1.MongoDBCommunity, expectedO
// resource has the correct Update Strategy
func StatefulSetHasUpdateStrategy(mdb *mdbv1.MongoDBCommunity, strategy appsv1.StatefulSetUpdateStrategyType) func(t *testing.T) {
return func(t *testing.T) {
err := e2eutil.WaitForStatefulSetToHaveUpdateStrategy(t, mdb, strategy, time.Second*15, time.Minute*8)
err := wait.ForStatefulSetToHaveUpdateStrategy(t, mdb, strategy, time.Second*15, time.Minute*8)
if err != nil {
t.Fatal(err)
}
Expand All @@ -147,7 +149,7 @@ func StatefulSetHasUpdateStrategy(mdb *mdbv1.MongoDBCommunity, strategy appsv1.S
// MongoDBReachesRunningPhase ensure the MongoDB resource reaches the Running phase
func MongoDBReachesRunningPhase(mdb *mdbv1.MongoDBCommunity) func(t *testing.T) {
return func(t *testing.T) {
err := e2eutil.WaitForMongoDBToReachPhase(t, mdb, mdbv1.Running, time.Second*15, time.Minute*12)
err := wait.ForMongoDBToReachPhase(t, mdb, mdbv1.Running, time.Second*15, time.Minute*12)
if err != nil {
t.Fatal(err)
}
Expand All @@ -158,7 +160,7 @@ func MongoDBReachesRunningPhase(mdb *mdbv1.MongoDBCommunity) func(t *testing.T)
// MongoDBReachesFailed ensure the MongoDB resource reaches the Failed phase.
func MongoDBReachesFailedPhase(mdb *mdbv1.MongoDBCommunity) func(t *testing.T) {
return func(t *testing.T) {
err := e2eutil.WaitForMongoDBToReachPhase(t, mdb, mdbv1.Failed, time.Second*15, time.Minute*5)
err := wait.ForMongoDBToReachPhase(t, mdb, mdbv1.Failed, time.Second*15, time.Minute*5)
if err != nil {
t.Fatal(err)
}
Expand All @@ -168,7 +170,7 @@ func MongoDBReachesFailedPhase(mdb *mdbv1.MongoDBCommunity) func(t *testing.T) {

func AutomationConfigSecretExists(mdb *mdbv1.MongoDBCommunity) func(t *testing.T) {
return func(t *testing.T) {
s, err := e2eutil.WaitForSecretToExist(mdb.AutomationConfigSecretName(), time.Second*5, time.Minute*1, mdb.Namespace)
s, err := wait.ForSecretToExist(mdb.AutomationConfigSecretName(), time.Second*5, time.Minute*1, mdb.Namespace)
assert.NoError(t, err)

t.Logf("Secret %s/%s was successfully created", mdb.AutomationConfigSecretName(), mdb.Namespace)
Expand Down Expand Up @@ -218,7 +220,7 @@ func CreateMongoDBResource(mdb *mdbv1.MongoDBCommunity, ctx *e2eutil.Context) fu
func GetConnectionStringSecret(mdb mdbv1.MongoDBCommunity, user scram.User) corev1.Secret {
secret := corev1.Secret{}
secretNamespacedName := types.NamespacedName{Name: user.GetConnectionStringSecretName(mdb), Namespace: mdb.Namespace}
e2eutil.TestClient.Get(context.TODO(), secretNamespacedName, &secret)
_ = e2eutil.TestClient.Get(context.TODO(), secretNamespacedName, &secret)
return secret
}

Expand Down Expand Up @@ -353,15 +355,15 @@ func StatefulSetContainerConditionIsTrue(mdb *mdbv1.MongoDBCommunity, containerN
func PodContainerBecomesNotReady(mdb *mdbv1.MongoDBCommunity, podNum int, containerName string) func(*testing.T) {
return func(t *testing.T) {
pod := podFromMongoDBCommunity(mdb, podNum)
assert.NoError(t, e2eutil.WaitForPodReadiness(t, false, containerName, time.Minute*10, pod))
assert.NoError(t, wait.ForPodReadiness(t, false, containerName, time.Minute*10, pod))
}
}

// PodContainerBecomesReady waits until the container with 'containerName' in the pod #podNum becomes ready.
func PodContainerBecomesReady(mdb *mdbv1.MongoDBCommunity, podNum int, containerName string) func(*testing.T) {
return func(t *testing.T) {
pod := podFromMongoDBCommunity(mdb, podNum)
assert.NoError(t, e2eutil.WaitForPodReadiness(t, true, containerName, time.Minute*3, pod))
assert.NoError(t, wait.ForPodReadiness(t, true, containerName, time.Minute*3, pod))
}
}

Expand Down
138 changes: 138 additions & 0 deletions test/e2e/util/wait/wait.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package wait
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code moved as is, with the Wait prefix removed from function names.


import (
"context"
"testing"
"time"

mdbv1 "github.com/mongodb/mongodb-kubernetes-operator/api/v1"
"github.com/mongodb/mongodb-kubernetes-operator/pkg/kube/statefulset"
e2eutil "github.com/mongodb/mongodb-kubernetes-operator/test/e2e"
"github.com/pkg/errors"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
)

// ForConfigMapToExist waits until a ConfigMap of the given name exists
// using the provided retryInterval and timeout
func ForConfigMapToExist(cmName string, retryInterval, timeout time.Duration) (corev1.ConfigMap, error) {
cm := corev1.ConfigMap{}
return cm, waitForRuntimeObjectToExist(cmName, retryInterval, timeout, &cm, e2eutil.OperatorNamespace)
}

// ForSecretToExist waits until a Secret of the given name exists
// using the provided retryInterval and timeout
func ForSecretToExist(cmName string, retryInterval, timeout time.Duration, namespace string) (corev1.Secret, error) {
s := corev1.Secret{}
return s, waitForRuntimeObjectToExist(cmName, retryInterval, timeout, &s, namespace)
}

// ForMongoDBToReachPhase waits until the given MongoDB resource reaches the expected phase
func ForMongoDBToReachPhase(t *testing.T, mdb *mdbv1.MongoDBCommunity, phase mdbv1.Phase, retryInterval, timeout time.Duration) error {
return waitForMongoDBCondition(mdb, retryInterval, timeout, func(db mdbv1.MongoDBCommunity) bool {
t.Logf("current phase: %s, waiting for phase: %s", db.Status.Phase, phase)
return db.Status.Phase == phase
})
}

// waitForMongoDBCondition polls and waits for a given condition to be true
func waitForMongoDBCondition(mdb *mdbv1.MongoDBCommunity, retryInterval, timeout time.Duration, condition func(mdbv1.MongoDBCommunity) bool) error {
mdbNew := mdbv1.MongoDBCommunity{}
return wait.Poll(retryInterval, timeout, func() (done bool, err error) {
err = e2eutil.TestClient.Get(context.TODO(), mdb.NamespacedName(), &mdbNew)
if err != nil {
return false, err
}
ready := condition(mdbNew)
return ready, nil
})
}

// ForStatefulSetToExist waits until a StatefulSet of the given name exists
// using the provided retryInterval and timeout
func ForStatefulSetToExist(stsName string, retryInterval, timeout time.Duration, namespace string) (appsv1.StatefulSet, error) {
sts := appsv1.StatefulSet{}
return sts, waitForRuntimeObjectToExist(stsName, retryInterval, timeout, &sts, namespace)
}

// ForStatefulSetToHaveUpdateStrategy waits until all replicas of the StatefulSet with the given name
// have reached the ready status
func ForStatefulSetToHaveUpdateStrategy(t *testing.T, mdb *mdbv1.MongoDBCommunity, strategy appsv1.StatefulSetUpdateStrategyType, retryInterval, timeout time.Duration) error {
return waitForStatefulSetCondition(t, mdb, retryInterval, timeout, func(sts appsv1.StatefulSet) bool {
return sts.Spec.UpdateStrategy.Type == strategy
})
}

// ForStatefulSetToBeReady waits until all replicas of the StatefulSet with the given name
// have reached the ready status
func ForStatefulSetToBeReady(t *testing.T, mdb *mdbv1.MongoDBCommunity, retryInterval, timeout time.Duration) error {
return waitForStatefulSetCondition(t, mdb, retryInterval, timeout, func(sts appsv1.StatefulSet) bool {
return statefulset.IsReady(sts, mdb.Spec.Members)
})
}

// ForStatefulSetToBeUnready waits until all replicas of the StatefulSet with the given name
// is not ready.
func ForStatefulSetToBeUnready(t *testing.T, mdb *mdbv1.MongoDBCommunity, retryInterval, timeout time.Duration) error {
return waitForStatefulSetCondition(t, mdb, retryInterval, timeout, func(sts appsv1.StatefulSet) bool {
return !statefulset.IsReady(sts, mdb.Spec.Members)
})
}

// ForStatefulSetToBeReadyAfterScaleDown waits for just the ready replicas to be correct
// and does not account for the updated replicas
func ForStatefulSetToBeReadyAfterScaleDown(t *testing.T, mdb *mdbv1.MongoDBCommunity, retryInterval, timeout time.Duration) error {
return waitForStatefulSetCondition(t, mdb, retryInterval, timeout, func(sts appsv1.StatefulSet) bool {
return int32(mdb.Spec.Members) == sts.Status.ReadyReplicas
})
}

func waitForStatefulSetCondition(t *testing.T, mdb *mdbv1.MongoDBCommunity, retryInterval, timeout time.Duration, condition func(set appsv1.StatefulSet) bool) error {
_, err := ForStatefulSetToExist(mdb.Name, retryInterval, timeout, mdb.Namespace)
if err != nil {
return errors.Errorf("error waiting for stateful set to be created: %s", err)
}

sts := appsv1.StatefulSet{}
return wait.Poll(retryInterval, timeout, func() (done bool, err error) {
err = e2eutil.TestClient.Get(context.TODO(), mdb.NamespacedName(), &sts)
if err != nil {
return false, err
}
t.Logf("Waiting for %s to have %d replicas. Current ready replicas: %d, Current updated replicas: %d, Current generation: %d, Observed Generation: %d\n",
mdb.Name, mdb.Spec.Members, sts.Status.ReadyReplicas, sts.Status.UpdatedReplicas, sts.Generation, sts.Status.ObservedGeneration)
ready := condition(sts)
return ready, nil
})
}

func ForPodReadiness(t *testing.T, isReady bool, containerName string, timeout time.Duration, pod corev1.Pod) error {
return wait.Poll(time.Second*3, timeout, func() (done bool, err error) {
err = e2eutil.TestClient.Get(context.TODO(), types.NamespacedName{Name: pod.Name, Namespace: pod.Namespace}, &pod)
if err != nil {
return false, err
}
for _, status := range pod.Status.ContainerStatuses {
t.Logf("%s (%s), ready: %v\n", pod.Name, status.Name, status.Ready)
if status.Name == containerName && status.Ready == isReady {
return true, nil
}
}
return false, nil
})
}

// waitForRuntimeObjectToExist waits until a runtime.Object of the given name exists
// using the provided retryInterval and timeout provided.
func waitForRuntimeObjectToExist(name string, retryInterval, timeout time.Duration, obj client.Object, namespace string) error {
return wait.Poll(retryInterval, timeout, func() (done bool, err error) {
err = e2eutil.TestClient.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: namespace}, obj)
if err != nil {
return false, client.IgnoreNotFound(err)
}
return true, nil
})
}