Skip to content

Commit 4418773

Browse files
authored
K8SPSMDB-1456 Fix replset init retry after userAdmin creation (#2365)
* K8SPSMDB-1456 Fix replset init retry after userAdmin creation * K8SPSMDB-1456 Check userAdmin auth before bootstrap createUser * K8SPSMDB-1456 Extract userAdmin bootstrap helper * K8SPSMDB-1456 Refine userAdmin bootstrap auth handling
1 parent 934015c commit 4418773

2 files changed

Lines changed: 462 additions & 13 deletions

File tree

pkg/controller/perconaservermongodb/mgo.go

Lines changed: 78 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -755,21 +755,10 @@ func (r *ReconcilePerconaServerMongoDB) handleReplsetInit(ctx context.Context, c
755755
}
756756
log.Info("replset initialized", "replset", replsetName, "pod", pod.Name)
757757

758-
log.Info("creating user admin", "replset", replsetName, "pod", pod.Name, "user", api.RoleUserAdmin)
759-
userAdmin, err := getInternalCredentials(ctx, r.client, cr, api.RoleUserAdmin)
760-
if err != nil {
761-
return nil, nil, errors.Wrap(err, "failed to get userAdmin credentials")
758+
if err := r.createUserAdminIfNeeded(ctx, cr, &pod, replsetName, mongoCmd); err != nil {
759+
return nil, nil, err
762760
}
763761

764-
cmd[2] = fmt.Sprintf(`%s --eval %s`, mongoCmd, mongoInitAdminUser(userAdmin.Username, userAdmin.Password))
765-
errb.Reset()
766-
outb.Reset()
767-
err = r.clientcmd.Exec(ctx, &pod, "mongod", cmd, nil, &outb, &errb, false)
768-
if err != nil {
769-
return nil, nil, fmt.Errorf("exec add admin user: %v / %s / %s", err, outb.String(), errb.String())
770-
}
771-
log.Info("user admin created", "replset", replsetName, "pod", pod.Name, "user", api.RoleUserAdmin)
772-
773762
return &pod, &api.ReplsetMemberStatus{
774763
Name: member.Host,
775764
State: mongo.MemberStatePrimary,
@@ -780,6 +769,82 @@ func (r *ReconcilePerconaServerMongoDB) handleReplsetInit(ctx context.Context, c
780769
return nil, nil, errNoRunningMongodContainers
781770
}
782771

772+
func (r *ReconcilePerconaServerMongoDB) createUserAdminIfNeeded(ctx context.Context, cr *api.PerconaServerMongoDB, pod *corev1.Pod, replsetName, mongoCmd string) error {
773+
log := logf.FromContext(ctx)
774+
775+
userAdmin, err := getInternalCredentials(ctx, r.client, cr, api.RoleUserAdmin)
776+
if err != nil {
777+
return errors.Wrap(err, "failed to get userAdmin credentials")
778+
}
779+
780+
canAuth, err := r.userAdminCanAuthenticate(ctx, pod, mongoCmd, userAdmin.Username, userAdmin.Password)
781+
if err != nil {
782+
return err
783+
}
784+
if canAuth {
785+
log.Info("user admin already exists and can authenticate, skipping creation", "replset", replsetName, "pod", pod.Name, "user", api.RoleUserAdmin)
786+
return nil
787+
}
788+
789+
log.Info("creating user admin", "replset", replsetName, "pod", pod.Name, "user", api.RoleUserAdmin)
790+
791+
var outb, errb bytes.Buffer
792+
cmd := []string{
793+
"sh", "-c",
794+
fmt.Sprintf(`%s --eval %s`, mongoCmd, mongoInitAdminUser(userAdmin.Username, userAdmin.Password)),
795+
}
796+
797+
err = r.clientcmd.Exec(ctx, pod, "mongod", cmd, nil, &outb, &errb, false)
798+
if err != nil {
799+
canAuth, authErr := r.userAdminCanAuthenticate(ctx, pod, mongoCmd, userAdmin.Username, userAdmin.Password)
800+
if authErr != nil {
801+
return fmt.Errorf("exec add admin user: %v / %s / %s; check userAdmin authentication: %v", err, outb.String(), errb.String(), authErr)
802+
}
803+
if !canAuth {
804+
return fmt.Errorf("exec add admin user: %v / %s / %s", err, outb.String(), errb.String())
805+
}
806+
log.Info("user admin can authenticate after createUser error, continuing", "replset", replsetName, "pod", pod.Name, "user", api.RoleUserAdmin)
807+
return nil
808+
}
809+
810+
log.Info("user admin created", "replset", replsetName, "pod", pod.Name, "user", api.RoleUserAdmin)
811+
return nil
812+
}
813+
814+
func (r *ReconcilePerconaServerMongoDB) userAdminCanAuthenticate(ctx context.Context, pod *corev1.Pod, mongoCmd, user, pwd string) (bool, error) {
815+
log := logf.FromContext(ctx)
816+
817+
var outb, errb bytes.Buffer
818+
cmd := []string{
819+
"sh", "-c",
820+
fmt.Sprintf(
821+
`%s --quiet -u '%s' -p '%s' --authenticationDatabase admin --eval 'quit(db.adminCommand({connectionStatus: 1}).authInfo.authenticatedUsers.length > 0 ? 0 : 1)'`,
822+
mongoCmd,
823+
strings.ReplaceAll(user, "'", `'"'"'`),
824+
strings.ReplaceAll(pwd, "'", `'"'"'`),
825+
),
826+
}
827+
828+
if err := r.clientcmd.Exec(ctx, pod, "mongod", cmd, nil, &outb, &errb, false); err != nil {
829+
if isMongoAuthFailure(err, outb.String(), errb.String()) {
830+
log.V(1).Info("userAdmin authentication failed", "pod", pod.Name, "error", err, "stdout", outb.String(), "stderr", errb.String())
831+
return false, nil
832+
}
833+
834+
return false, fmt.Errorf("exec userAdmin authentication check: %v / %s / %s", err, outb.String(), errb.String())
835+
}
836+
837+
return true, nil
838+
}
839+
840+
func isMongoAuthFailure(err error, stdout, stderr string) bool {
841+
msg := strings.ToLower(strings.Join([]string{err.Error(), stdout, stderr}, " "))
842+
return strings.Contains(msg, "authentication failed") ||
843+
strings.Contains(msg, "auth failed") ||
844+
strings.Contains(msg, "requires authentication") ||
845+
strings.Contains(msg, "unauthorized")
846+
}
847+
783848
func (r *ReconcilePerconaServerMongoDB) handleReplicaSetNoPrimary(ctx context.Context, cr *api.PerconaServerMongoDB, replset *api.ReplsetSpec, pods []corev1.Pod) error {
784849
log := logf.FromContext(ctx).WithName("handleReplicaSetNoPrimary")
785850

0 commit comments

Comments
 (0)