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) {
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
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",
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