func SyncUsers()

in oracle/controllers/databasecontroller/database_resources.go [206:320]


func SyncUsers(ctx context.Context, r *DatabaseReconciler, db *v1alpha1.Database, cdbName string, log logr.Logger) error {
	log.Info("resources/syncUsers: sync database users requested", "db", db)
	r.Recorder.Eventf(db, corev1.EventTypeNormal, k8s.SyncingUser, fmt.Sprintf("Syncing users for database %q", db.Spec.Name))

	var userSpecs []*controllers.User
	var usernames []string
	userVerMap := make(map[string]string)
	// Copy pdb admin user version into local map to sync later.
	if v, ok := db.Status.UserResourceVersions[pdbAdminUserName]; ok {
		userVerMap[pdbAdminUserName] = v
	}
	for _, user := range db.Spec.Users {
		var privs []string
		usernames = append(usernames, user.Name)
		for _, specPriv := range user.Privileges {
			privs = append(privs, string(specPriv))
		}
		userSpec := &controllers.User{
			Name:       user.Name,
			Privileges: privs,
		}
		// database_controller.validateSpec has validated the spec earlier;
		// So no duplicated validation here.
		if user.Password != "" {
			userVerMap[user.Name] = user.Password
			userSpec.Password = user.Password
			lastPwd, ok := db.Status.UserResourceVersions[user.Name]
			if ok {
				userSpec.LastPassword = lastPwd
			}
		}
		if user.GsmSecretRef != nil {
			userVerMap[user.Name] = fmt.Sprintf(gsmResourceVersionString, user.GsmSecretRef.ProjectId, user.GsmSecretRef.SecretId, user.GsmSecretRef.Version)
			ref := &controllers.GsmSecretReference{
				ProjectId: user.GsmSecretRef.ProjectId,
				SecretId:  user.GsmSecretRef.SecretId,
				Version:   user.GsmSecretRef.Version,
			}
			if lastVer, ok := db.Status.UserResourceVersions[user.Name]; ok {
				ref.LastVersion = lastVer
			}
			userSpec.PasswordGsmSecretRef = ref
		}
		userSpecs = append(userSpecs, userSpec)
	}
	req := &controllers.UsersChangedRequest{
		PdbName:   db.Spec.Name,
		UserSpecs: userSpecs,
	}
	resp, err := controllers.UsersChanged(ctx, r, r.DatabaseClientFactory, db.GetNamespace(), db.Spec.Instance, *req)
	if err != nil {
		log.Error(err, "resources/syncUsers: failed on UsersChanged gRPC call")
		return err
	}

	if resp.Changed {
		db.Status.Phase = commonv1alpha1.DatabaseUpdating
		db.Status.Conditions = k8s.Upsert(db.Status.Conditions, k8s.UserReady, v1.ConditionFalse, k8s.SyncInProgress, "")
		if err := r.Status().Update(ctx, db); err != nil {
			return err
		}
		log.Info("resources/syncUsers: update database users requested", "CDB", cdbName, "PDB", db.Spec.Name)
		req := &controllers.UpdateUsersRequest{
			PdbName:   db.Spec.Name,
			UserSpecs: userSpecs,
		}
		if err := controllers.UpdateUsers(ctx, r, r.DatabaseClientFactory, db.GetNamespace(), db.Spec.Instance, *req); err != nil {
			log.Error(err, "resources/syncUsers: failed on UpdateUser gRPC call")
			return err
		}
		log.Info("resources/syncUsers: update database users done", "CDB", cdbName, "PDB", db.Spec.Name)
	}
	log.Info("resources/syncUsers: sync database users done", "CDB", cdbName, "PDB", db.Spec.Name)

	userReady := &v1.Condition{
		Type:    k8s.UserReady,
		Status:  v1.ConditionTrue,
		Reason:  k8s.SyncComplete,
		Message: "",
	}

	if len(resp.Suppressed) != 0 {
		userReady.Status = v1.ConditionFalse
		userReady.Reason = k8s.UserOutOfSync
		var msg []string
		for _, u := range resp.Suppressed {
			if u.SuppressType == controllers.UsersChangedResponse_DELETE {
				msg = append(msg, fmt.Sprintf("User %q not defined in database spec, "+
					"supposed to be deleted. suppressed SQL %q. Fix by deleting the user in DB or updating DB spec to include the user", u.UserName, u.Sql))
			} else if u.SuppressType == controllers.UsersChangedResponse_CREATE {
				msg = append(msg, fmt.Sprintf("User %q cannot be created, "+
					"password is not provided. Fix by creating the user in DB or updating DB spec to include password", u.UserName))
			}
		}
		userReady.Message = strings.Join(msg, ".")
	}

	if k8s.ConditionStatusEquals(userReady, v1.ConditionTrue) {
		r.Recorder.Eventf(db, corev1.EventTypeNormal, k8s.SyncedUser, fmt.Sprintf("Synced users for database %q", db.Spec.Name))
	} else {
		r.Recorder.Eventf(db, corev1.EventTypeWarning, k8s.FailedToSyncUser, fmt.Sprintf("Failed to sync users for database %q, %s", db.Spec.Name, userReady.Message))
	}

	db.Status.Conditions = k8s.Upsert(db.Status.Conditions, userReady.Type, userReady.Status, userReady.Reason, userReady.Message)
	db.Status.UserResourceVersions = userVerMap
	db.Status.UserNames = usernames[0:integer.IntMin(3, len(usernames))]
	if len(usernames) > 3 {
		db.Status.UserNames = append(db.Status.UserNames, "...")
	}
	r.updateIsChangeApplied(ctx, db)
	if err := r.Status().Update(ctx, db); err != nil {
		return err
	}
	return nil
}