...

Source file src/edge-infra.dev/pkg/edge/datasync/controllers/couchctl/server_controller_cloud_test.go

Documentation: edge-infra.dev/pkg/edge/datasync/controllers/couchctl

     1  package couchctl
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"testing"
     7  
     8  	"github.com/stretchr/testify/require"
     9  	"gotest.tools/v3/assert/cmp"
    10  
    11  	persistenceApi "edge-infra.dev/pkg/edge/apis/persistence/v1alpha1"
    12  	"edge-infra.dev/pkg/edge/constants/api/cluster"
    13  	"edge-infra.dev/pkg/edge/constants/api/fleet"
    14  	dsapi "edge-infra.dev/pkg/edge/datasync/apis/v1alpha1"
    15  	"edge-infra.dev/pkg/edge/datasync/couchdb"
    16  	"edge-infra.dev/pkg/k8s/object"
    17  	"edge-infra.dev/pkg/k8s/testing/kmp"
    18  	v1ien "edge-infra.dev/pkg/sds/ien/k8s/apis/v1"
    19  	nodemeta "edge-infra.dev/pkg/sds/ien/node"
    20  	"edge-infra.dev/test/f2"
    21  	"edge-infra.dev/test/f2/x/ktest"
    22  
    23  	corev1 "k8s.io/api/core/v1"
    24  	netv1 "k8s.io/api/networking/v1"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  
    27  	"sigs.k8s.io/controller-runtime/pkg/client"
    28  )
    29  
    30  func TestCloudServerController(t *testing.T) { //nolint:dupl
    31  	fin := f2.NewFeature("CouchServerReconciler Cloud").
    32  		WithLabel(_fleetType, fleet.CouchDB).
    33  		WithLabel(_clusterType, cluster.GKE).
    34  		Setup("CouchDBServer Exists For Cloud Server", func(ctx f2.Context, t *testing.T) f2.Context {
    35  			k := ktest.FromContextT(ctx, t)
    36  			k.WaitOn(t, k.ObjExists(couchDBServer))
    37  			return ctx
    38  		}).
    39  		Test("Embedded Manifests Applied", func(ctx f2.Context, t *testing.T) f2.Context {
    40  			k := ktest.FromContextT(ctx, t)
    41  			cm, err := ConfigMap(*couchDBServer)
    42  			require.NoError(t, err)
    43  			k.WaitOn(t, k.ObjExists(cm))
    44  			return ctx
    45  		}).
    46  		Test("Ingress In Valid State", func(ctx f2.Context, t *testing.T) f2.Context {
    47  			k := ktest.FromContextT(ctx, t)
    48  			ingress := &netv1.Ingress{
    49  				ObjectMeta: metav1.ObjectMeta{
    50  					Name:      couchdb.CouchIngressName,
    51  					Namespace: couchDBServer.Namespace,
    52  				},
    53  			}
    54  			k.WaitOn(t, k.ObjExists(ingress))
    55  			k.WaitOn(t, k.Check(ingress, func(_ client.Object) cmp.Result {
    56  				ingresses := ingress.Status.LoadBalancer.Ingress
    57  				if len(ingresses) > 0 && ingresses[0].IP != "" {
    58  					return cmp.ResultSuccess
    59  				}
    60  				return cmp.ResultFailure("expected ingress IP was not found")
    61  			}))
    62  			return ctx
    63  		}).
    64  		Test("Admin Secret Created", func(ctx f2.Context, t *testing.T) f2.Context {
    65  			k := ktest.FromContextT(ctx, t)
    66  			secret := secretObj(couchDBServer.Spec.Admin.Credentials.Namespace, couchDBServer.Spec.Admin.Credentials.Name)
    67  			secretKeys := []string{couchdb.SecretUsername, couchdb.SecretPassword, couchdb.SecretAdminsIni, couchdb.SecretCookieName}
    68  			k.WaitOn(t, k.ObjExists(secret))
    69  			k.WaitOn(t, k.Check(secret, secretDataExists(secretKeys...)))
    70  			return ctx
    71  		}).
    72  		Test("Couchdb Pods Ready", func(ctx f2.Context, t *testing.T) f2.Context {
    73  			k := ktest.FromContextT(ctx, t)
    74  			podNum := couchDBServer.Spec.Cluster.Nodes
    75  			for i := 0; i < podNum; i++ {
    76  				pod := &corev1.Pod{
    77  					ObjectMeta: metav1.ObjectMeta{
    78  						Name:      fmt.Sprintf("%s-%d", couchdb.Namespace, i),
    79  						Namespace: couchdb.Namespace,
    80  					},
    81  				}
    82  				k.WaitOn(t, k.Check(pod, kmp.IsCurrent()))
    83  			}
    84  			return ctx
    85  		}).
    86  		Test("CouchDBServer Successfully", func(ctx f2.Context, t *testing.T) f2.Context {
    87  			k := ktest.FromContextT(ctx, t)
    88  			k.WaitOn(t, k.Check(couchDBServer, kmp.IsReady()))
    89  			return ctx
    90  		}).
    91  		Test("CouchDB Cluster Setup", func(ctx f2.Context, t *testing.T) f2.Context {
    92  			k := ktest.FromContextT(ctx, t)
    93  
    94  			creds, err := serverAdminCreds(ctx, k.Client, couchDBServer)
    95  			require.NoError(t, err)
    96  
    97  			clusterURI := couchdb.FormatFinishClusterURI(
    98  				string(creds.Username),
    99  				string(creds.Password),
   100  				couchDBServer.Spec.URI,
   101  				couchCtlConfig.CouchDBPort)
   102  
   103  			require.Eventually(t, func() bool {
   104  				finished, err := checkFinishStatus(clusterURI)
   105  				require.NoError(t, err)
   106  				return finished
   107  			}, k.Timeout, k.Tick, "couchdb cluster not set up")
   108  
   109  			return ctx
   110  		}).
   111  		Feature()
   112  
   113  	f.Test(t, fin)
   114  }
   115  
   116  // fakePodsForCouchdbServer create pods to test locally
   117  func fakePodsForCouchdbServer(ctx context.Context, cl client.Client, server *dsapi.CouchDBServer) error {
   118  	podPrefix := couchdb.Namespace
   119  	var labels map[string]string
   120  	if server.IsTouchPoint() {
   121  		podPrefix = fmt.Sprintf("%s-%s", couchdb.Namespace, laneNumber)
   122  		labels = map[string]string{
   123  			persistenceApi.InstanceLabel: podPrefix,
   124  			nodemeta.ClassLabel:          string(v1ien.Touchpoint),
   125  		}
   126  	}
   127  	podNum := server.Spec.Cluster.Nodes
   128  	for i := 0; i < podNum; i++ {
   129  		pod := &corev1.Pod{
   130  			ObjectMeta: metav1.ObjectMeta{
   131  				Name:      fmt.Sprintf("%s-%d", podPrefix, i),
   132  				Namespace: couchdb.Namespace,
   133  				Labels:    labels,
   134  			},
   135  			Spec: corev1.PodSpec{
   136  				Containers: []corev1.Container{{
   137  					Name:  "couchdb",
   138  					Image: "podinfo", // for testing locally
   139  				}},
   140  			},
   141  		}
   142  		err := cl.Create(ctx, pod)
   143  		if client.IgnoreAlreadyExists(err) != nil {
   144  			return err
   145  		}
   146  		pod.Status = corev1.PodStatus{
   147  			Conditions: []corev1.PodCondition{{
   148  				Type:   "Ready",
   149  				Status: corev1.ConditionTrue,
   150  			}},
   151  		}
   152  		err = cl.Status().Update(ctx, pod)
   153  		if err != nil {
   154  			return err
   155  		}
   156  	}
   157  	return nil
   158  }
   159  
   160  func secretObj(namespace, name string) *corev1.Secret {
   161  	return &corev1.Secret{
   162  		ObjectMeta: metav1.ObjectMeta{
   163  			Name:      name,
   164  			Namespace: namespace,
   165  		},
   166  	}
   167  }
   168  
   169  func secretDataExists(keys ...string) kmp.Komparison {
   170  	return func(o client.Object) cmp.Result {
   171  		s := o.(*corev1.Secret)
   172  		for _, secretKey := range keys {
   173  			data, ok := s.Data[secretKey]
   174  			if !ok || len(data) == 0 {
   175  				return cmp.ResultFailure(fmt.Sprintf(
   176  					"%s doesnt contain key %s", object.FmtObject(s), secretKey),
   177  				)
   178  			}
   179  		}
   180  		return cmp.ResultSuccess
   181  	}
   182  }
   183  

View as plain text