@@ -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+
783848func (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