package couchctl import ( "context" "fmt" "testing" "github.com/stretchr/testify/require" "gotest.tools/v3/assert/cmp" persistenceApi "edge-infra.dev/pkg/edge/apis/persistence/v1alpha1" "edge-infra.dev/pkg/edge/constants/api/cluster" "edge-infra.dev/pkg/edge/constants/api/fleet" dsapi "edge-infra.dev/pkg/edge/datasync/apis/v1alpha1" "edge-infra.dev/pkg/edge/datasync/couchdb" "edge-infra.dev/pkg/k8s/object" "edge-infra.dev/pkg/k8s/testing/kmp" v1ien "edge-infra.dev/pkg/sds/ien/k8s/apis/v1" nodemeta "edge-infra.dev/pkg/sds/ien/node" "edge-infra.dev/test/f2" "edge-infra.dev/test/f2/x/ktest" corev1 "k8s.io/api/core/v1" netv1 "k8s.io/api/networking/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" ) func TestCloudServerController(t *testing.T) { //nolint:dupl fin := f2.NewFeature("CouchServerReconciler Cloud"). WithLabel(_fleetType, fleet.CouchDB). WithLabel(_clusterType, cluster.GKE). Setup("CouchDBServer Exists For Cloud Server", func(ctx f2.Context, t *testing.T) f2.Context { k := ktest.FromContextT(ctx, t) k.WaitOn(t, k.ObjExists(couchDBServer)) return ctx }). Test("Embedded Manifests Applied", func(ctx f2.Context, t *testing.T) f2.Context { k := ktest.FromContextT(ctx, t) cm, err := ConfigMap(*couchDBServer) require.NoError(t, err) k.WaitOn(t, k.ObjExists(cm)) return ctx }). Test("Ingress In Valid State", func(ctx f2.Context, t *testing.T) f2.Context { k := ktest.FromContextT(ctx, t) ingress := &netv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: couchdb.CouchIngressName, Namespace: couchDBServer.Namespace, }, } k.WaitOn(t, k.ObjExists(ingress)) k.WaitOn(t, k.Check(ingress, func(_ client.Object) cmp.Result { ingresses := ingress.Status.LoadBalancer.Ingress if len(ingresses) > 0 && ingresses[0].IP != "" { return cmp.ResultSuccess } return cmp.ResultFailure("expected ingress IP was not found") })) return ctx }). Test("Admin Secret Created", func(ctx f2.Context, t *testing.T) f2.Context { k := ktest.FromContextT(ctx, t) secret := secretObj(couchDBServer.Spec.Admin.Credentials.Namespace, couchDBServer.Spec.Admin.Credentials.Name) secretKeys := []string{couchdb.SecretUsername, couchdb.SecretPassword, couchdb.SecretAdminsIni, couchdb.SecretCookieName} k.WaitOn(t, k.ObjExists(secret)) k.WaitOn(t, k.Check(secret, secretDataExists(secretKeys...))) return ctx }). Test("Couchdb Pods Ready", func(ctx f2.Context, t *testing.T) f2.Context { k := ktest.FromContextT(ctx, t) podNum := couchDBServer.Spec.Cluster.Nodes for i := 0; i < podNum; i++ { pod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: fmt.Sprintf("%s-%d", couchdb.Namespace, i), Namespace: couchdb.Namespace, }, } k.WaitOn(t, k.Check(pod, kmp.IsCurrent())) } return ctx }). Test("CouchDBServer Successfully", func(ctx f2.Context, t *testing.T) f2.Context { k := ktest.FromContextT(ctx, t) k.WaitOn(t, k.Check(couchDBServer, kmp.IsReady())) return ctx }). Test("CouchDB Cluster Setup", func(ctx f2.Context, t *testing.T) f2.Context { k := ktest.FromContextT(ctx, t) creds, err := serverAdminCreds(ctx, k.Client, couchDBServer) require.NoError(t, err) clusterURI := couchdb.FormatFinishClusterURI( string(creds.Username), string(creds.Password), couchDBServer.Spec.URI, couchCtlConfig.CouchDBPort) require.Eventually(t, func() bool { finished, err := checkFinishStatus(clusterURI) require.NoError(t, err) return finished }, k.Timeout, k.Tick, "couchdb cluster not set up") return ctx }). Feature() f.Test(t, fin) } // fakePodsForCouchdbServer create pods to test locally func fakePodsForCouchdbServer(ctx context.Context, cl client.Client, server *dsapi.CouchDBServer) error { podPrefix := couchdb.Namespace var labels map[string]string if server.IsTouchPoint() { podPrefix = fmt.Sprintf("%s-%s", couchdb.Namespace, laneNumber) labels = map[string]string{ persistenceApi.InstanceLabel: podPrefix, nodemeta.ClassLabel: string(v1ien.Touchpoint), } } podNum := server.Spec.Cluster.Nodes for i := 0; i < podNum; i++ { pod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: fmt.Sprintf("%s-%d", podPrefix, i), Namespace: couchdb.Namespace, Labels: labels, }, Spec: corev1.PodSpec{ Containers: []corev1.Container{{ Name: "couchdb", Image: "podinfo", // for testing locally }}, }, } err := cl.Create(ctx, pod) if client.IgnoreAlreadyExists(err) != nil { return err } pod.Status = corev1.PodStatus{ Conditions: []corev1.PodCondition{{ Type: "Ready", Status: corev1.ConditionTrue, }}, } err = cl.Status().Update(ctx, pod) if err != nil { return err } } return nil } func secretObj(namespace, name string) *corev1.Secret { return &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, }, } } func secretDataExists(keys ...string) kmp.Komparison { return func(o client.Object) cmp.Result { s := o.(*corev1.Secret) for _, secretKey := range keys { data, ok := s.Data[secretKey] if !ok || len(data) == 0 { return cmp.ResultFailure(fmt.Sprintf( "%s doesnt contain key %s", object.FmtObject(s), secretKey), ) } } return cmp.ResultSuccess } }