func syncMembers(
	reqLogger logr.Logger,
	cr *kdv1.KubeDirectorCluster,
	roles []*roleInfo,
	configmetaGenerator func(string) string,
) error {
	allMembersUpdated := true
	checkGenOk := func(stateMembers []*kdv1.MemberStatus) bool {
		for _, member := range stateMembers {
			if member.StateDetail.LastConfigDataGeneration == nil {
				continue
			}
			if *member.StateDetail.LastConfigDataGeneration == *cr.Status.SpecGenerationToProcess {
				continue
			}
			return false
		}
		return true
	}
	for _, r := range roles {
		if ready, readyOk := r.membersByState[memberReady]; readyOk {
			handleReadyMembers(reqLogger, cr, r, configmetaGenerator)
			if allMembersUpdated {
				allMembersUpdated = checkGenOk(ready)
			}
		}
		if allMembersUpdated {
			if createPending, createPendingOk := r.membersByState[memberCreatePending]; createPendingOk {
				allMembersUpdated = checkGenOk(createPending)
			}
		}
		if allMembersUpdated {
			if creating, creatingOk := r.membersByState[memberCreating]; creatingOk {
				allMembersUpdated = checkGenOk(creating)
			}
		}
	}
	if !allMembersUpdated {
		for _, r := range roles {
			if _, ok := r.membersByState[memberCreating]; ok {
				handleCreatingMembers(reqLogger, cr, r, roles, nil)
			}
		}
		shared.LogInfo(
			reqLogger,
			cr,
			shared.EventReasonCluster,
			"cluster spec change processing blocked on member updates",
		)
		return nil
	}
	for _, r := range roles {
		if _, ok := r.membersByState[memberCreatePending]; ok {
			handleCreatePendingMembers(reqLogger, cr, r)
		}
		if _, ok := r.membersByState[memberCreating]; ok {
			handleCreatingMembers(reqLogger, cr, r, roles, configmetaGenerator)
		}
		if _, ok := r.membersByState[memberDeletePending]; ok {
			handleDeletePendingMembers(reqLogger, cr, r, roles)
		}
		if _, ok := r.membersByState[memberDeleting]; ok {
			handleDeletingMembers(reqLogger, cr, r)
		}
	}
	return nil
}
syncMembers is responsible for adding or deleting members. It and
syncMemberNotifies are the only functions in this file that are invoked
from another file (from the syncCluster function in cluster.go). Along with
k8s interactions (changing statefulset replica count), this involves
creating notifications to existing members about additions/deletions,
injecting configmeta data into members, and triggering application setup.
This function will modify the member status data structures to update their
states.
package kubedirectorcluster
import (
	"bufio"
	"errors"
	"fmt"
	"math"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"github.com/go-logr/logr"
	kdv1 "github.com/bluek8s/kubedirector/pkg/apis/kubedirector/v1beta1"
	"github.com/bluek8s/kubedirector/pkg/catalog"
	"github.com/bluek8s/kubedirector/pkg/executor"
	"github.com/bluek8s/kubedirector/pkg/observer"
	"github.com/bluek8s/kubedirector/pkg/shared"
	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/client-go/util/exec"
)