1 package couchctl
2
3 import (
4 "testing"
5
6 appsv1 "k8s.io/api/apps/v1"
7 corev1 "k8s.io/api/core/v1"
8 "sigs.k8s.io/yaml"
9
10 "edge-infra.dev/pkg/edge/apis/meta"
11 persistenceApi "edge-infra.dev/pkg/edge/apis/persistence/v1alpha1"
12 "edge-infra.dev/pkg/edge/controllers/envctl/pkg/nameutils"
13 dsapi "edge-infra.dev/pkg/edge/datasync/apis/v1alpha1"
14 "edge-infra.dev/pkg/edge/datasync/couchdb"
15 "edge-infra.dev/pkg/k8s/unstructured"
16 v1ien "edge-infra.dev/pkg/sds/ien/k8s/apis/v1"
17 nodemeta "edge-infra.dev/pkg/sds/ien/node"
18
19 "github.com/stretchr/testify/assert"
20 )
21
22 var (
23 replSet = `apiVersion: datasync.edge.ncr.com/v1alpha1
24 kind: CouchDBReplicationSet
25 metadata:
26 name: "{server_name}"
27 namespace: data-sync-couchdb
28 spec:
29 datasets:
30 - name: "{replication_db}"
31 config:
32 doc_ids:
33 - repl_doc
34 provider:
35 name: ""
36 source:
37 name: "{node_uid}"
38 namespace: "{replication_secret_ns}"
39 target:
40 name: "{server_name}"
41 namespace: data-sync-couchdb
42 `
43 replSetInvalid = `apiVersion: datasync.edge.ncr.com/v1alpha1
44 kind: CouchDBReplicationSet
45 metadata:
46 name: "{anything_goes}"
47 namespace: data-sync-couchdb
48 `
49 statefulSetTemplate = `apiVersion: datasync.edge.ncr.com/v1alpha1
50 kind: CouchDBPersistence
51 metadata:
52 name: couchdb-lane
53 spec:
54 statefulsets:
55 - apiVersion: apps/v1
56 kind: StatefulSet
57 metadata:
58 name: "{couchdb_sts}"
59 namespace: data-sync-couchdb
60 labels:
61 platform.edge.ncr.com/component: data-sync-couchdb
62 spec:
63 replicas: 1
64 selector:
65 matchLabels:
66 platform.edge.ncr.com/component: data-sync-couchdb
67 template:
68 metadata:
69 labels:
70 platform.edge.ncr.com/component: data-sync-couchdb
71 spec:
72 containers:
73 - name: nignx
74 image: nginx
75 volumes:
76 - name: config
77 configMap:
78 name: "{couchdb_sts}"
79 items:
80 - key: inifile
81 path: chart.ini
82 volumeClaimTemplates:
83 - metadata:
84 name: database-storage
85 labels:
86 platform.edge.ncr.com/component: data-sync-couchdb
87 spec:
88 resources:
89 requests:
90 storage: "10Gi"
91 accessModes:
92 - "ReadWriteOnce"
93 serviceName: data-sync-couchdb
94 podManagementPolicy: Parallel
95 `
96 rdb = "repl-hash-banner-id"
97 )
98
99 func TestParseSubstitutions(t *testing.T) {
100 substitutions, err := ParseSubstitutions(replSet)
101 assert.NoError(t, err)
102 assert.Equal(t, len(substitutions), 4)
103
104 substitutions, err = ParseSubstitutions(replSetInvalid)
105 assert.Error(t, err)
106 assert.Nil(t, substitutions)
107 }
108
109 func TestApplySubstitutions(t *testing.T) {
110 suVars := map[SubstitutionVar]string{
111 ServerName: couchdb.StoreServerName,
112 ServerType: string(dsapi.Store),
113 LaneNumber: "",
114 Suffix: "",
115 CouchDBStatefulSet: couchdb.Namespace,
116 ChirpName: ChirpOldName,
117 ReplicationDB: "repl-db",
118 ReplicationSecret: couchdb.StoreReplicationSecretName,
119 ReplicationSecretNS: ControllerNamespace,
120 NodeUID: "uid",
121 }
122 su := ToSubstitution(suVars)
123
124 r := &dsapi.CouchDBReplicationSet{}
125 assert.NoError(t, yaml.Unmarshal([]byte(replSet), r))
126 un, err := ApplySubstitutions(r, su)
127 assert.NoError(t, err)
128 assert.NotNil(t, un)
129 assert.NoError(t, unstructured.FromUnstructured(un, r))
130 assert.Equal(t, r.Name, couchdb.StoreServerName)
131 assert.Equal(t, r.Spec.Datasets[0].Name, "repl-db")
132 assert.Equal(t, r.Spec.Source.Name, "uid")
133 assert.Equal(t, r.Spec.Source.Namespace, ControllerNamespace)
134 assert.Equal(t, r.Spec.Target.Name, couchdb.StoreServerName)
135 assert.Equal(t, r.Labels[couchdb.SubstitutionLabel], "true")
136 }
137
138 func TestStoreSubstitution(t *testing.T) {
139
140 su := StoreSubstitution(rdb)
141 assert.Equal(t, su.ServerName, couchdb.StoreServerName)
142 assert.Equal(t, su.ServerType, dsapi.Store)
143 assert.Equal(t, su.LaneNumber, "")
144 assert.Equal(t, su.CouchDBStatefulSet, couchdb.Namespace)
145 assert.Equal(t, su.ChirpStatefulSet, ChirpOldName)
146 assert.Equal(t, su.ReplicationDB, rdb)
147 assert.Equal(t, su.ReplicationSecret, couchdb.StoreReplicationSecretName)
148 assert.Equal(t, su.ReplicationSecretNS, ControllerNamespace)
149 }
150
151 func TestLaneSubstitution(t *testing.T) {
152 ni := &nameutils.NodeInfo{
153 UID: "leader-uid",
154 Hostname: defaultHost,
155 Lane: "",
156 Class: v1ien.Server,
157 Role: v1ien.ControlPlane,
158 }
159 su := LaneSubstitution(ni, nil, rdb, "leader-uid")
160 assert.True(t, su.Leader)
161 assert.Equal(t, su.NodeInfo, ni)
162 assert.Equal(t, su.ServerName, "couchdb-leader-uid")
163 assert.Equal(t, su.ServerType, dsapi.Store)
164 assert.Equal(t, su.LaneNumber, "")
165 assert.Equal(t, su.CouchDBStatefulSet, "couchdb-"+meta.Hash("leader-uid"))
166 assert.Equal(t, su.ChirpStatefulSet, "chirp-"+meta.Hash("leader-uid"))
167 assert.Equal(t, su.ReplicationDB, rdb)
168 assert.Equal(t, su.ReplicationSecret, couchdb.StoreReplicationSecretName)
169 assert.Equal(t, su.ReplicationSecretNS, ControllerNamespace)
170 assert.Equal(t, su.NodeUID, "leader-uid")
171
172 ni = &nameutils.NodeInfo{
173 UID: "uid",
174 Name: "test",
175 Hostname: "ien",
176 Lane: "1",
177 Class: v1ien.Touchpoint,
178 Role: v1ien.Worker,
179 }
180 m := map[string]map[string]string{}
181 m["data-sync-couchdb"] = map[string]string{
182 "test": "1",
183 }
184 m["data-sync-messaging"] = map[string]string{
185 "test": "1",
186 }
187
188
189 su = LaneSubstitution(ni, m, rdb, "leader-uid")
190 assert.False(t, su.Leader)
191 assert.Equal(t, su.NodeInfo, ni)
192 assert.Equal(t, su.ServerName, "couchdb-uid")
193 assert.Equal(t, su.ServerType, dsapi.Touchpoint)
194 assert.Equal(t, su.LaneNumber, "1")
195 assert.Equal(t, su.CouchDBStatefulSet, "data-sync-couchdb-1")
196 assert.Equal(t, su.ChirpStatefulSet, "data-sync-messaging-1")
197 assert.Equal(t, su.ReplicationDB, rdb)
198 assert.Equal(t, su.ReplicationSecret, "couchdb-leader-uid")
199 assert.Equal(t, su.ReplicationSecretNS, couchdb.Namespace)
200 assert.Equal(t, su.NodeUID, "uid")
201 }
202
203 func TestStoreStatefulSetSubstitution(t *testing.T) {
204 su := StoreSubstitution(rdb)
205
206 p := &dsapi.CouchDBPersistence{}
207 err := yaml.Unmarshal([]byte(statefulSetTemplate), p)
208 assert.NoError(t, err)
209 sts := &p.Spec.StatefulSets[0]
210
211
212 un, err := ApplySubstitutions(sts, su)
213 assert.NoError(t, err)
214 assert.NotNil(t, un)
215
216 sts = &appsv1.StatefulSet{}
217 err = unstructured.FromUnstructured(un, sts)
218 assert.NoError(t, err)
219
220 assert.Equal(t, sts.Name, su.CouchDBStatefulSet)
221 assert.Equal(t, sts.Spec.Selector.MatchLabels[persistenceApi.InstanceLabel], sts.Name)
222 assert.Equal(t, sts.Spec.Template.ObjectMeta.Labels[persistenceApi.InstanceLabel], sts.Name)
223 assert.NotContains(t, sts.Spec.Template.ObjectMeta.Labels, nodemeta.ClassLabel)
224 assert.Equal(t, sts.Spec.Template.Spec.Volumes[0].ConfigMap.Name, su.CouchDBStatefulSet)
225
226 na := sts.Spec.Template.Spec.Affinity.NodeAffinity
227
228 assert.Nil(t, na.RequiredDuringSchedulingIgnoredDuringExecution)
229 assert.Len(t, na.PreferredDuringSchedulingIgnoredDuringExecution, 1)
230 sch := na.PreferredDuringSchedulingIgnoredDuringExecution[0]
231 assert.Equal(t, int32(100), sch.Weight)
232 assert.Empty(t, sch.Preference.MatchFields)
233 assert.Len(t, sch.Preference.MatchExpressions, 1)
234 assert.Equal(t, sch.Preference.MatchExpressions[0], corev1.NodeSelectorRequirement{
235 Key: nodemeta.RoleLabel,
236 Operator: corev1.NodeSelectorOpIn,
237 Values: []string{string(v1ien.ControlPlane)},
238 })
239 }
240
241 func TestLaneStatefulSetSubstitution(t *testing.T) {
242 ni := &nameutils.NodeInfo{
243 UID: "uid",
244 Hostname: defaultHost,
245 Lane: "1",
246 Class: v1ien.Touchpoint,
247 Role: v1ien.Worker,
248 }
249 su := LaneSubstitution(ni, nil, rdb, "leader-uid")
250
251 p := &dsapi.CouchDBPersistence{}
252 err := yaml.Unmarshal([]byte(statefulSetTemplate), p)
253 assert.NoError(t, err)
254 sts := &p.Spec.StatefulSets[0]
255
256
257 un, err := ApplySubstitutions(sts, su)
258 assert.NoError(t, err)
259 assert.NotNil(t, un)
260
261 sts = &appsv1.StatefulSet{}
262 err = unstructured.FromUnstructured(un, sts)
263 assert.NoError(t, err)
264
265 assert.Equal(t, sts.Name, su.CouchDBStatefulSet)
266 assert.Equal(t, sts.Labels[couchdb.NodeUIDLabel], "uid")
267 assert.Equal(t, sts.Spec.Selector.MatchLabels[persistenceApi.InstanceLabel], sts.Name)
268 assert.Equal(t, sts.Spec.Template.ObjectMeta.Labels[persistenceApi.InstanceLabel], sts.Name)
269 assert.Equal(t, sts.Spec.Template.Spec.Volumes[0].ConfigMap.Name, su.CouchDBStatefulSet)
270
271 na := sts.Spec.Template.Spec.Affinity.NodeAffinity
272 assert.Nil(t, na.PreferredDuringSchedulingIgnoredDuringExecution)
273 assert.NotNil(t, na.RequiredDuringSchedulingIgnoredDuringExecution)
274 nodeSelectors := na.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms
275
276 assert.Len(t, nodeSelectors, 1)
277
278 nodeSelector := nodeSelectors[0]
279 assert.Empty(t, nodeSelector.MatchFields)
280 assert.Len(t, nodeSelector.MatchExpressions, 1)
281
282 assert.Equal(t, nodeSelector.MatchExpressions[0], corev1.NodeSelectorRequirement{
283 Key: couchdb.NodeUIDLabel,
284 Operator: corev1.NodeSelectorOpIn,
285 Values: []string{su.NodeUID},
286 })
287 }
288
View as plain text