1 package couchctl
2
3 import (
4 "context"
5 "encoding/json"
6 "errors"
7 "fmt"
8 "reflect"
9 "time"
10
11 "edge-infra.dev/pkg/k8s/runtime/inventory"
12 unstructuredutil "edge-infra.dev/pkg/k8s/unstructured"
13
14 corev1 "k8s.io/api/core/v1"
15 kerrors "k8s.io/apimachinery/pkg/api/errors"
16 v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
17 "k8s.io/client-go/dynamic"
18 kuberecorder "k8s.io/client-go/tools/record"
19
20 "sigs.k8s.io/cli-utils/pkg/kstatus/watcher"
21 ctrl "sigs.k8s.io/controller-runtime"
22 "sigs.k8s.io/controller-runtime/pkg/builder"
23 "sigs.k8s.io/controller-runtime/pkg/client"
24 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
25 "sigs.k8s.io/controller-runtime/pkg/predicate"
26
27 "edge-infra.dev/pkg/edge/api/utils"
28 dsapi "edge-infra.dev/pkg/edge/datasync/apis/v1alpha1"
29 "edge-infra.dev/pkg/edge/datasync/couchdb"
30 "edge-infra.dev/pkg/k8s/meta/status"
31 "edge-infra.dev/pkg/k8s/runtime/conditions"
32 "edge-infra.dev/pkg/k8s/runtime/controller/metrics"
33 "edge-infra.dev/pkg/k8s/runtime/controller/reconcile"
34 "edge-infra.dev/pkg/k8s/runtime/controller/reconcile/recerr"
35 "edge-infra.dev/pkg/k8s/runtime/patch"
36 "edge-infra.dev/pkg/k8s/runtime/sap"
37
38 "github.com/go-logr/logr"
39 )
40
41 type CouchUserReconciler struct {
42 client.Client
43 NodeResourcePredicate
44 kuberecorder.EventRecorder
45 SecretManager secretManager
46 Name string
47 Config *Config
48 Metrics metrics.Metrics
49 patchOptions []patch.Option
50 ResourceManager *sap.ResourceManager
51 replicationDB string
52 }
53
54 var (
55 ErrNewUserNotFoundInDB = errors.New("new user not found in users table")
56
57 userConditions = reconcile.Conditions{
58 Target: status.ReadyCondition,
59 Owned: []string{
60 dsapi.UserSetupSucceededReason,
61 status.ReadyCondition,
62 status.ReconcilingCondition,
63 status.StalledCondition,
64 },
65 Summarize: []string{
66 dsapi.UserSetupSucceededReason,
67 status.StalledCondition,
68 },
69 NegativePolarity: []string{
70 status.ReconcilingCondition,
71 status.StalledCondition,
72 },
73 }
74 edgeAdminNamespaces = []string{couchdb.Namespace, "cushion", "edge-iam"}
75 )
76
77
78 func (r *CouchUserReconciler) SetupWithManager(mgr ctrl.Manager) error {
79 r.replicationDB = r.Config.ReplicationDB()
80 r.patchOptions = getPatchOptions(serverConditions.Owned, r.Name)
81 d, err := dynamic.NewForConfig(mgr.GetConfig())
82 if err != nil {
83 return fmt.Errorf("fail to create dynamic client: %w", err)
84 }
85 r.ResourceManager = sap.NewResourceManager(
86 r.Client,
87 watcher.NewDefaultStatusWatcher(d, mgr.GetRESTMapper()),
88 sap.Owner{Field: r.Name},
89 )
90 return ctrl.NewControllerManagedBy(mgr).
91 For(&dsapi.CouchDBUser{}, r.userPredicates()).
92 Owns(&corev1.Secret{}, r.secretPredicates()).
93 Complete(r)
94 }
95
96 func (r *CouchUserReconciler) userPredicates() builder.Predicates {
97 return builder.WithPredicates(
98 predicate.GenerationChangedPredicate{},
99 predicate.NewPredicateFuncs(func(obj client.Object) bool {
100 if r.Config.IsDSDS() {
101 return r.ShouldReconcile(r.Config, obj)
102 }
103 return true
104 }))
105 }
106
107 func (r *CouchUserReconciler) secretPredicates() builder.Predicates {
108 return builder.WithPredicates(
109 predicate.GenerationChangedPredicate{},
110 predicate.NewPredicateFuncs(func(obj client.Object) bool {
111 if r.Config.IsDSDS() {
112 list := dsapi.CouchDBUserList{}
113 if err := r.Client.List(context.Background(), &list,
114 client.InNamespace(obj.GetNamespace()), client.MatchingLabels{
115 couchdb.NodeUIDLabel: r.Config.NodeUID,
116 }); err != nil {
117 return false
118 }
119 if len(list.Items) == 0 {
120 return false
121 }
122 return isChild(obj, list.Items)
123 }
124 return true
125 }))
126 }
127
128 func isChild(obj client.Object, items []dsapi.CouchDBUser) bool {
129
130 secretID := fmt.Sprintf("%s_%s__Secret", obj.GetNamespace(), obj.GetName())
131 for _, item := range items {
132 if item.Status.Inventory == nil || item.Status.Inventory.Entries == nil {
133 continue
134 }
135 for _, entry := range item.Status.Inventory.Entries {
136 if entry.ID == secretID {
137 return true
138 }
139 }
140 }
141 return false
142 }
143
144 func (r *CouchUserReconciler) Reconcile(ctx context.Context, req ctrl.Request) (res ctrl.Result, err error) {
145 reconcileStart := time.Now()
146 log := ctrl.LoggerFrom(ctx)
147
148 user := &dsapi.CouchDBUser{}
149 if err := r.Client.Get(ctx, req.NamespacedName, user); err != nil {
150 return ctrl.Result{}, client.IgnoreNotFound(err)
151 }
152 user.WithRetry(r.Config.RequeueTime)
153 user.WithInterval(r.Config.PollingInterval)
154 withUserType(user)
155
156 ctx = logr.NewContext(ctx, log)
157
158 patcher := patch.NewSerialPatcher(user, r.Client)
159 if err := reconcile.Progressing(ctx, user, patcher, r.patchOptions...); err != nil {
160 log.Error(err, "unable to update status")
161 return ctrl.Result{}, err
162 }
163
164 recResult := reconcile.ResultEmpty
165 var recErr recerr.Error
166
167 defer func() {
168 summarizer := reconcile.NewSummarizer(patcher)
169 res, err = summarizer.SummarizeAndPatch(ctx, user, []reconcile.SummarizeOption{
170 reconcile.WithConditions(userConditions),
171 reconcile.WithResult(recResult),
172 reconcile.WithError(recErr),
173 reconcile.WithIgnoreNotFound(),
174 reconcile.WithProcessors(
175 reconcile.RecordResult,
176 ),
177 reconcile.WithFieldOwner(r.Name),
178 reconcile.WithEventRecorder(r.EventRecorder),
179 }...)
180 r.Metrics.RecordDuration(ctx, user, reconcileStart)
181 r.Metrics.RecordReadiness(ctx, user)
182 }()
183
184
185 if !controllerutil.ContainsFinalizer(user, DatasyncFinalizer) {
186 controllerutil.AddFinalizer(user, DatasyncFinalizer)
187
188
189 recResult = reconcile.ResultRequeue
190 return
191 }
192
193 if !user.ObjectMeta.DeletionTimestamp.IsZero() {
194 recErr = r.finalize(ctx, user)
195 return
196 }
197
198 if recErr = r.reconcile(ctx, user); recErr != nil {
199
200 if !couchDBNotReadyOrNotFound(recErr) {
201 recErr.ToCondition(user, dsapi.UserSetupSucceededReason)
202 err = recErr
203 return
204 }
205 }
206 recResult = reconcile.ResultSuccess
207 conditions.MarkTrue(user, dsapi.UserSetupSucceededReason, status.SucceededReason, "Successfully created CouchDB User")
208 log.Info("Successfully created CouchDB User")
209
210 return
211 }
212
213
214 func (r *CouchUserReconciler) finalize(ctx context.Context, user *dsapi.CouchDBUser) (recErr recerr.Error) {
215 log := logr.FromContextOrDiscard(ctx)
216 log.Info("running finalizer")
217
218 var prune bool
219 defer func() {
220 if prune {
221 log.Info("pruning user's resources")
222 err := pruneInventory(ctx, r.ResourceManager, user)
223 if err != nil {
224 recErr = recerr.New(err, status.DependencyInvalidReason)
225 }
226 controllerutil.RemoveFinalizer(user, DatasyncFinalizer)
227 log.Info("finalizer ran successfully")
228 }
229 }()
230
231 nodeFound, err := r.nodeExistsAndSchedulable(ctx, user)
232 if err != nil {
233 return recerr.New(err, status.DependencyInvalidReason)
234 }
235
236
237 if !nodeFound {
238 prune = true
239 return
240 }
241
242
243 cc, err := couchDBClient(ctx, r.Client, r.Config, user)
244 switch {
245 case err != nil && serverNotFound(err):
246 prune = true
247 return
248 case err != nil:
249 return recerr.New(err, status.DependencyInvalidReason)
250 }
251
252
253 username := user.Spec.User.Name
254 if username == "" {
255 up := &couchdb.UsernamePassword{}
256 _, err = up.FromSecret(ctx, r.Client, user.SecretNamespacedName())
257 switch {
258 case err != nil && kerrors.IsNotFound(err):
259 prune = true
260 return
261 case err != nil:
262 return recerr.New(err, status.DependencyInvalidReason)
263 }
264 username = string(up.Username)
265 }
266
267 err = cc.DeleteUser(ctx, username)
268 if err != nil && !couchdb.IsNotFound(err) {
269 logError(ctx, err, "fail to delete user from couchdb", "username", username)
270 return recerr.New(err, status.DependencyInvalidReason)
271 }
272 prune = true
273 return
274 }
275
276
277
278 func (r *CouchUserReconciler) nodeExistsAndSchedulable(ctx context.Context, user *dsapi.CouchDBUser) (bool, error) {
279 var nodeUID string
280 _, server, err := checkIfServerIsReady(ctx, r.Client, user)
281 switch {
282 case err != nil && !kerrors.IsNotFound(err):
283 return false, err
284 case server != nil:
285 nodeUID = labelOrEmpty(server, couchdb.NodeUIDLabel)
286 default:
287 nodeUID = labelOrEmpty(user, couchdb.NodeUIDLabel)
288 }
289
290 if nodeUID == "" {
291 return false, nil
292 }
293
294 nodes := &corev1.NodeList{}
295 err = r.Client.List(ctx, nodes, &client.ListOptions{})
296 if err != nil {
297 return false, err
298 }
299 for _, node := range nodes.Items {
300 if nodeUID == string(node.UID) {
301 return !node.Spec.Unschedulable, nil
302 }
303 }
304 return false, nil
305 }
306
307 func (r *CouchUserReconciler) reconcile(ctx context.Context, user *dsapi.CouchDBUser) recerr.Error {
308 log := logr.FromContextOrDiscard(ctx)
309
310
311 ready, server, err := checkIfServerIsReady(ctx, r.Client, user)
312 if err != nil {
313 return recerr.NewWait(err, status.DependencyNotReadyReason, r.Config.ServerNotReady)
314 }
315
316 if !ready {
317 err := fmt.Errorf("%w", ErrServerNotReady)
318 return recerr.NewWait(err, status.DependencyNotReadyReason, r.Config.ServerNotReady)
319 }
320
321 log = log.WithValues("server", client.ObjectKeyFromObject(server))
322 ctx = logr.NewContext(ctx, log)
323
324
325 return r.reconcileUser(ctx, user, server)
326 }
327
328 func (r *CouchUserReconciler) authorizeUser(user *dsapi.CouchDBUser) (bool, error) {
329 if user.IsAdminCredentials() && !utils.Contains(edgeAdminNamespaces, user.Namespace) {
330 return false, fmt.Errorf("admin users can only be created in edge admin namespaces: %v", edgeAdminNamespaces)
331 }
332 return true, nil
333 }
334
335 func (r *CouchUserReconciler) reconcileUser(ctx context.Context, user *dsapi.CouchDBUser, server *dsapi.CouchDBServer) recerr.Error {
336 log := logr.FromContextOrDiscard(ctx)
337
338
339 changeSet := sap.NewChangeSet()
340
341
342 cc, err := couchDBClient(ctx, r.Client, r.Config, user)
343 switch {
344 case err != nil && serverNotFound(err):
345 return recerr.NewWait(err, status.DependencyNotReadyReason, r.Config.ServerNotReady)
346 case err != nil:
347 logError(ctx, err, "error creating couchdb client", "NamespacedName", server.AdminCredentials())
348 return recerr.NewWait(err, status.DependencyInvalidReason, r.Config.ServerNotReady)
349 }
350
351
352 defer cc.Close(ctx)
353
354 authorized, err := r.authorizeUser(user)
355 if !authorized {
356 return recerr.NewStalled(err, dsapi.UserCreationFailedReason)
357 }
358
359
360
361
362
363 userCreds := r.credentials(user, server)
364
365
366
367 var cs *sap.ChangeSetEntry
368 secretNN := user.SecretNamespacedName()
369 secret, err := userCreds.FromSecret(ctx, r.Client, secretNN)
370 switch {
371 case err != nil && canMigrate(err):
372 var ownerRefs []v1.OwnerReference
373 if user.IsUserCredentials() {
374 ownerRefs = couchDBUserOwnerReference(user)
375 }
376 secret, err := userCreds.ToSecret(ctx, r.Client, secretNN, ownerRefs...)
377 if err != nil {
378 logError(ctx, err, "fail to create couchdb k8 secret", "NamespacedName", secretNN)
379 return recerr.New(err, dsapi.UserCreationFailedReason)
380 }
381 un, err := unstructuredutil.ToUnstructured(secret)
382 if err != nil {
383 return recerr.New(err, dsapi.UserCreationFailedReason)
384 }
385 cs, err = r.ResourceManager.Apply(ctx, un, sap.ApplyOptions{})
386 if err != nil {
387 return recerr.New(err, dsapi.UserCreationFailedReason)
388 }
389 changeSet.Add(*cs)
390 case err != nil:
391 log.Error(err, "fail to get couchdb k8 secret", "NamespacedName", secretNN)
392 return recerr.New(err, status.DependencyInvalidReason)
393 default:
394 cs, err = ExistingChangeSetEntry(secret)
395 if err != nil {
396 return recerr.New(err, status.DependencyInvalidReason)
397 }
398 changeSet.Add(*cs)
399 }
400
401 usernamePassword := usernamePassword(userCreds, user)
402 userName := string(usernamePassword.Username)
403 log = log.WithValues("username", userName)
404
405
406 _, err = cc.CreateNewUser(ctx, userName, string(usernamePassword.Password), user.Spec.User.Roles)
407 if err != nil && !errors.Is(err, couchdb.ErrConflict) {
408 logError(ctx, err, "fail to create couchdb user", "NamespacedName", secretNN)
409 return recerr.NewWait(err, dsapi.UserFailedToAddUserReason, r.Config.ServerNotReady)
410 }
411 var roles []string
412 if !user.IsUserCredentials() {
413 roles = user.Spec.User.Roles
414 }
415
416 userExists, err := cc.CheckUserAndRolesExists(ctx, userName, roles)
417 if !userExists || errors.Is(err, couchdb.ErrNotFound) {
418 err := fmt.Errorf("%w", ErrNewUserNotFoundInDB)
419 logError(ctx, err, "fail to check roles for couchdb user, user or role foes not exists", "NamespacedName", secretNN)
420 return recerr.NewWait(err, dsapi.UserFailedToAddUserReason, r.Config.ServerNotReady)
421 }
422 if err != nil {
423 log.Error(err, "fail to check roles for couchdb user", "NamespacedName", secretNN)
424 return recerr.NewWait(err, dsapi.UserFailedToAddUserReason, r.Config.ServerNotReady)
425 }
426
427
428
429
430
431 if hasDBSecurityRole(user) {
432 recErr := r.reconcileRoleBasedUser(ctx, user, cc, userName)
433 if recErr != nil {
434 return recErr
435 }
436 }
437
438
439 if server.IsCloud() && user.AddToSecretManager() {
440 userRef := corev1.SecretReference{Namespace: user.Namespace, Name: user.Name}
441 couchReplicationCred := server.ReplicationCredentials() == userRef
442 err = r.CredsToSecretManager(ctx, user, userCreds, couchReplicationCred)
443 if err != nil {
444 return recerr.New(err, string(dsapi.ReplicationCreationFailedStatus))
445 }
446 }
447
448
449
450 i := inventory.New(inventory.FromSapChangeSet(changeSet))
451 if err := r.prune(ctx, user, i); err != nil {
452 return recerr.New(err, dsapi.PruneFailed)
453 }
454 user.Status.Inventory = i
455
456 return nil
457 }
458
459 func (r *CouchUserReconciler) prune(ctx context.Context, user *dsapi.CouchDBUser, i *inventory.ResourceInventory) error {
460 log := logr.FromContextOrDiscard(ctx)
461 if user.Status.Inventory != nil {
462 diff, err := inventory.Diff(user.Status.Inventory, i)
463 if err != nil {
464 return nil
465 }
466 if len(diff) > 0 {
467 changeSet, err := r.ResourceManager.DeleteAll(ctx, diff, sap.DefaultDeleteOptions())
468 if err != nil {
469 return err
470 }
471 log.Info("pruned objects", "changeset", changeSet.ToMap())
472 }
473 }
474 return nil
475 }
476
477
478 func (r *CouchUserReconciler) reconcileRoleBasedUser(
479 ctx context.Context,
480 user *dsapi.CouchDBUser,
481 cc *couchdb.CouchDB,
482 userName string) recerr.Error {
483 log := logr.FromContextOrDiscard(ctx)
484
485 replDB := r.Config.ReplicationDB()
486 replSet := &dsapi.ReplicationSet{}
487 if err := cc.GetReplicationSetDoc(ctx, replDB, replSet); err != nil {
488 log.Info("replication doc not found, skipping creation of replication creds", "dbname", replDB)
489 return nil
490 }
491
492
493 if user.IsReplicationCredentials() {
494 if recErr := r.addUsernameToSecurity(ctx, user, cc, userName, replDB); recErr != nil {
495 return recErr
496 }
497 }
498
499 for _, d := range replSet.Datasets {
500 if !shouldReplicate(r.Config, d, r.replicationDB) {
501
502
503 continue
504 }
505 if !updateDBSecurityForUser(user, d) {
506 continue
507 }
508 if recErr := r.addUsernameToSecurity(ctx, user, cc, userName, d.Name); recErr != nil {
509 return recErr
510 }
511 }
512 return nil
513 }
514
515 func canMigrate(err error) bool {
516 return kerrors.IsNotFound(err) ||
517 errors.Is(err, couchdb.ErrCouchDBURIMissing) ||
518 errors.Is(err, couchdb.ErrDBNameMissing) ||
519 errors.Is(err, couchdb.ErrSecretDataMissing) ||
520 errors.Is(err, couchdb.ErrInvalidCookieSecret)
521 }
522
523 func (r *CouchUserReconciler) addUsernameToSecurity(
524 ctx context.Context,
525 user *dsapi.CouchDBUser,
526 cc *couchdb.CouchDB,
527 userName, dbName string) recerr.Error {
528
529 exists, err := cc.CheckIfDBExists(ctx, dbName)
530 if err != nil {
531
532 return recerr.NewWait(err, dsapi.DatabaseCreationFailedReason, r.Config.ServerNotReady)
533 }
534 if !exists {
535 err := fmt.Errorf("%w", ErrNewDatabaseNotFound)
536
537 return recerr.NewWait(err, dsapi.DatabaseCreationFailedReason, r.Config.DatabaseNotFound)
538 }
539 security := userToSecurity(user, userName)
540
541
542 exists, err = cc.CheckDBUsersAndRoles(ctx, security, dbName)
543 if err != nil {
544 err = fmt.Errorf("error checking for couchdb users and roles")
545 return recerr.NewWait(err, dsapi.UserCreationFailedReason, r.Config.DatabaseNotFound)
546 }
547 if exists {
548 return nil
549 }
550
551
552 err = cc.AddMemberUserAndRolesToDB(ctx, security, dbName)
553 if err != nil {
554 return recerr.NewWait(err, dsapi.DatabaseAddRolesFailedReason, r.Config.DatabaseNotFound)
555 }
556 return nil
557 }
558
559
560 func (r *CouchUserReconciler) CredsToSecretManager(ctx context.Context, user *dsapi.CouchDBUser, userCreds couchdb.CredentialsManager, couchReplicationCred bool) error {
561 sms, err := r.toSecretManagerSecret(userCreds)
562 if err != nil {
563 return fmt.Errorf("failed to convert secret to secret manager: %w", err)
564 }
565
566 value, err := json.Marshal(sms)
567 if err != nil {
568 return fmt.Errorf("failed to convert ReplicationCredentials to json string for Secret Manager: %w", err)
569 }
570
571
572 writer, err := r.SecretManager.NewWithOptions(ctx, r.Config.ProjectID)
573 if err != nil {
574 return err
575 }
576
577 var secretName string
578 if couchReplicationCred {
579 secretName = couchdb.ReplicationSMgrSecretName
580 } else {
581 secretName = fmt.Sprintf("%s-%s", user.Namespace, user.Name)
582 }
583 return writer.AddSecret(ctx, secretName, value, nil, false, nil, "")
584 }
585
586 func (r *CouchUserReconciler) toSecretManagerSecret(creds couchdb.CredentialsManager) (*couchdb.SecretManagerSecret, error) {
587 sms := &couchdb.SecretManagerSecret{}
588 switch s := creds.(type) {
589 case *couchdb.AdminCredentials:
590 sms.Username = string(s.Username)
591 sms.Password = string(s.Password)
592 case *couchdb.ReplicationCredentials:
593 sms.Username = string(s.Username)
594 sms.Password = string(s.Password)
595 sms.URI = string(s.URI)
596 sms.DBName = string(s.DBName)
597 case *couchdb.UserCredentials:
598 sms.Username = string(s.Username)
599 sms.Password = string(s.Password)
600 sms.URI = string(s.URI)
601 default:
602 return nil, fmt.Errorf("credentials of type %T not implemented for secret manager secret", s)
603 }
604 return sms, nil
605 }
606
607 func userToSecurity(user *dsapi.CouchDBUser, userName string) couchdb.Security {
608 userRoles := user.Spec.User.Roles
609 security := couchdb.Security{}
610 for _, role := range userRoles {
611 if role == couchdb.CreateViewUser {
612 addNameToSecurity(&security, userName, true)
613 } else if role == couchdb.ReadOnlyUser {
614 addNameToSecurity(&security, userName, false)
615 } else {
616 addNameToSecurity(&security, userName, false)
617 addRoleToSecurity(&security, role, false)
618 }
619 }
620 return security
621 }
622
623 func addNameToSecurity(security *couchdb.Security, name string, admin bool) {
624 if admin {
625 if !utils.Contains(security.Admins.Names, name) {
626 security.Admins.Names = append(security.Admins.Names, name)
627 }
628 } else {
629 if !utils.Contains(security.Members.Names, name) {
630 security.Members.Names = append(security.Members.Names, name)
631 }
632 }
633 }
634
635 func addRoleToSecurity(security *couchdb.Security, role string, admin bool) {
636 if admin {
637 if !utils.Contains(security.Admins.Roles, role) {
638 security.Admins.Roles = append(security.Admins.Roles, role)
639 }
640 } else {
641 if !utils.Contains(security.Members.Roles, role) {
642 security.Members.Roles = append(security.Members.Roles, role)
643 }
644 }
645 }
646
647 func hasDBSecurityRole(user *dsapi.CouchDBUser) bool {
648 return !user.Spec.Provider.Empty() || user.IsUserCredentials() || user.IsReplicationCredentials()
649 }
650
651
652 func withUserType(user *dsapi.CouchDBUser) {
653 if user.Spec.Type == "" {
654 switch {
655 case utils.Contains(user.Spec.User.Roles, couchdb.ReplicationUser):
656 user.Spec.Type = dsapi.ReplicationCredentials
657 case utils.Contains(user.Spec.User.Roles, couchdb.ReadOnlyUser) ||
658 utils.Contains(user.Spec.User.Roles, couchdb.CreateViewUser):
659 user.Spec.Type = dsapi.UserCredentials
660 default:
661 user.Spec.Type = dsapi.AdminCredentials
662 }
663 }
664 }
665
666 func updateDBSecurityForUser(user *dsapi.CouchDBUser, d dsapi.Dataset) bool {
667 if user.Spec.Type == dsapi.ReplicationCredentials {
668 return true
669 }
670 userProvider := user.Spec.Provider
671 dbProvider := d.Provider
672 if userProvider.Empty() && dbProvider.Empty() {
673 return true
674 }
675 if !userProvider.Empty() && !dbProvider.Empty() {
676 return userProvider.Name == dbProvider.Name
677 }
678 if dbProvider.Empty() {
679 return true
680 }
681 return false
682 }
683
684 func couchDBUserOwnerReference(user *dsapi.CouchDBUser) []v1.OwnerReference {
685 return []v1.OwnerReference{
686 *v1.NewControllerRef(user,
687 dsapi.GroupVersion.WithKind(reflect.TypeOf(dsapi.CouchDBUser{}).Name())),
688 }
689 }
690
691 func (r *CouchUserReconciler) credentials(user *dsapi.CouchDBUser, server *dsapi.CouchDBServer) couchdb.CredentialsManager {
692 up := couchdb.UsernamePassword{Username: []byte(user.Spec.User.Name)}
693 switch user.Spec.Type {
694 case dsapi.UserCredentials:
695 return &couchdb.UserCredentials{
696 UsernamePassword: up,
697 URI: []byte(getCouchDBUserURL(r.Config, server)),
698 }
699 case dsapi.ReplicationCredentials:
700 return &couchdb.ReplicationCredentials{
701 UserCredentials: couchdb.UserCredentials{
702 UsernamePassword: up,
703 URI: []byte(getServerURL(r.Config, server)),
704 },
705 DBName: []byte(r.Config.ReplicationDB()),
706 }
707 default:
708 return &couchdb.AdminCredentials{UsernamePassword: up}
709 }
710 }
711
712 func usernamePassword(creds couchdb.CredentialsManager, user *dsapi.CouchDBUser) couchdb.UsernamePassword {
713 switch user.Spec.Type {
714 case dsapi.UserCredentials:
715 return creds.(*couchdb.UserCredentials).UsernamePassword
716 case dsapi.ReplicationCredentials:
717 return creds.(*couchdb.ReplicationCredentials).UsernamePassword
718 default:
719 return creds.(*couchdb.AdminCredentials).UsernamePassword
720 }
721 }
722
View as plain text