func newVersionedStatefulSetPod(client client.Client, currentSet, updateSet *nuwav1.StatefulSet, currentRevision, updateRevision string, ordinal int) *v1.Pod {
	if currentSet.Spec.UpdateStrategy.Type == apps.RollingUpdateStatefulSetStrategyType &&
		(currentSet.Spec.UpdateStrategy.RollingUpdate == nil && ordinal < int(currentSet.Status.CurrentReplicas)) ||
		(currentSet.Spec.UpdateStrategy.RollingUpdate != nil && ordinal < int(*currentSet.Spec.UpdateStrategy.RollingUpdate.Partition)) {
		pod := newStatefulSetPod(client, currentSet, ordinal)
		setPodRevision(pod, currentRevision)
		return pod
	}
	pod := newStatefulSetPod(client, updateSet, ordinal)
	setPodRevision(pod, updateRevision)
	return pod
}
newVersionedStatefulSetPod creates a new Pod for a StatefulSet. currentSet is the representation of the set at the
current revision. updateSet is the representation of the set at the updateRevision. currentRevision is the name of
the current revision. updateRevision is the name of the update revision. ordinal is the ordinal of the Pod. If the
returned error is nil, the returned Pod is valid.
package controllers
import (
	"bytes"
	"encoding/json"
	"fmt"
	nuwav1 "github.com/yametech/nuwa/api/v1"
	apps "k8s.io/api/apps/v1"
	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/strategicpatch"
	podutil "k8s.io/kubernetes/pkg/api/v1/pod"
	"k8s.io/kubernetes/pkg/controller"
	"k8s.io/kubernetes/pkg/controller/history"
	"regexp"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"strconv"
)
var controllerKind = nuwav1.GroupVersion.WithKind("StatefulSet")
var patchCodec runtime.Codec
var updateExpectations = NewUpdateExpectations(func(o metav1.Object) string {
	p := o.(*v1.Pod)
	return getPodRevision(p)
})
var statefulPodRegex = regexp.MustCompile("(.*)-([0-9]+)$")
func getParentNameAndOrdinal(pod *v1.Pod) (string, int) {
	parent := ""
	ordinal := -1
	subMatches := statefulPodRegex.FindStringSubmatch(pod.Name)
	if len(subMatches) < 3 {
		return parent, ordinal
	}
	parent = subMatches[1]
	if i, err := strconv.ParseInt(subMatches[2], 10, 32); err == nil {
		ordinal = int(i)
	}
	return parent, ordinal
}
func getParentName(pod *v1.Pod) string {
	parent, _ := getParentNameAndOrdinal(pod)
	return parent
}
func getOrdinal(pod *v1.Pod) int {
	_, ordinal := getParentNameAndOrdinal(pod)
	return ordinal
}
func getPodName(set *nuwav1.StatefulSet, ordinal int) string {
	return fmt.Sprintf("%s-%d", set.Name, ordinal)
}
func getPersistentVolumeClaimName(set *nuwav1.StatefulSet, claim *v1.PersistentVolumeClaim, ordinal int) string {
	return fmt.Sprintf("%s-%s-%d", claim.Name, set.Name, ordinal)
}
func isMemberOf(set *nuwav1.StatefulSet, pod *v1.Pod) bool {
	return getParentName(pod) == set.Name
}
func identityMatches(set *nuwav1.StatefulSet, pod *v1.Pod) bool {
	parent, ordinal := getParentNameAndOrdinal(pod)
	return ordinal >= 0 &&
		set.Name == parent &&
		pod.Name == getPodName(set, ordinal) &&
		pod.Namespace == set.Namespace &&
		pod.Labels[apps.StatefulSetPodNameLabel] == pod.Name
}
func storageMatches(set *nuwav1.StatefulSet, pod *v1.Pod) bool {
	ordinal := getOrdinal(pod)
	if ordinal < 0 {
		return false
	}
	volumes := make(map[string]v1.Volume, len(pod.Spec.Volumes))
	for _, volume := range pod.Spec.Volumes {
		volumes[volume.Name] = volume
	}
	for _, claim := range set.Spec.VolumeClaimTemplates {
		volume, found := volumes[claim.Name]
		if !found ||
			volume.VolumeSource.PersistentVolumeClaim == nil ||
			volume.VolumeSource.PersistentVolumeClaim.ClaimName !=
				getPersistentVolumeClaimName(set, &claim, ordinal) {
			return false
		}
	}
	return true
}
func getPersistentVolumeClaims(set *nuwav1.StatefulSet, pod *v1.Pod) map[string]v1.PersistentVolumeClaim {
	ordinal := getOrdinal(pod)
	templates := set.Spec.VolumeClaimTemplates
	claims := make(map[string]v1.PersistentVolumeClaim, len(templates))
	for i := range templates {
		claim := templates[i]
		claim.Name = getPersistentVolumeClaimName(set, &claim, ordinal)
		claim.Namespace = set.Namespace
		claim.Labels = set.Spec.Selector.MatchLabels
		claims[templates[i].Name] = claim
	}
	return claims
}
func updateStorage(set *nuwav1.StatefulSet, pod *v1.Pod) {
	currentVolumes := pod.Spec.Volumes
	claims := getPersistentVolumeClaims(set, pod)
	newVolumes := make([]v1.Volume, 0, len(claims))
	for name, claim := range claims {
		newVolumes = append(newVolumes, v1.Volume{
			Name: name,
			VolumeSource: v1.VolumeSource{
				PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
					ClaimName: claim.Name,
					ReadOnly: false,
				},
			},
		})
	}
	for i := range currentVolumes {
		if _, ok := claims[currentVolumes[i].Name]; !ok {
			newVolumes = append(newVolumes, currentVolumes[i])
		}
	}
	pod.Spec.Volumes = newVolumes
}
func initIdentity(set *nuwav1.StatefulSet, pod *v1.Pod) {
	updateIdentity(set, pod)
	pod.Spec.Hostname = pod.Name
	pod.Spec.Subdomain = set.Spec.ServiceName
}
func updateIdentity(set *nuwav1.StatefulSet, pod *v1.Pod) {
	pod.Name = getPodName(set, getOrdinal(pod))
	pod.Namespace = set.Namespace
	if pod.Labels == nil {
		pod.Labels = make(map[string]string)
	}
	pod.Labels[apps.StatefulSetPodNameLabel] = pod.Name
}
func isRunningAndReady(pod *v1.Pod) bool {
	return pod.Status.Phase == v1.PodRunning && podutil.IsPodReady(pod)
}
func isCreated(pod *v1.Pod) bool {
	return pod.Status.Phase != ""
}
func isFailed(pod *v1.Pod) bool {
	return pod.Status.Phase == v1.PodFailed
}
func isTerminating(pod *v1.Pod) bool {
	return pod.DeletionTimestamp != nil
}
func isHealthy(pod *v1.Pod) bool {
	return isRunningAndReady(pod) && !isTerminating(pod)
}
func allowsBurst(set *nuwav1.StatefulSet) bool {
	return set.Spec.PodManagementPolicy == apps.ParallelPodManagement
}
func setPodRevision(pod *v1.Pod, revision string) {
	if pod.Labels == nil {
		pod.Labels = make(map[string]string)
	}
	pod.Labels[apps.StatefulSetRevisionLabel] = revision
}
func getPodRevision(pod *v1.Pod) string {
	if pod.Labels == nil {
		return ""
	}
	return pod.Labels[apps.StatefulSetRevisionLabel]
}
func newStatefulSetPod(client client.Client, set *nuwav1.StatefulSet, ordinal int) *v1.Pod {
	pod, _ := controller.GetPodFromTemplate(&set.Spec.Template, set, metav1.NewControllerRef(set, controllerKind))
	pod.Name = getPodName(set, ordinal)
	initIdentity(set, pod)
	updateStorage(set, pod)
	pod = coordinatorPod(client, set, pod, ordinal)
	return pod
}
func coordinatorPod(client client.Client, set *nuwav1.StatefulSet, pod *v1.Pod, ordinal int) *v1.Pod {
	if set.Spec.Template.Annotations == nil {
		return pod
	}
	if cords, ok := set.Spec.Template.Annotations["coordinates"]; ok {
		coordinates := make(nuwav1.Coordinates, 0)
		if err := json.Unmarshal([]byte(cords), &coordinates); err != nil {
			utilruntime.HandleError(fmt.Errorf("Statefulset %s/%s unmarshal coordiantes %s error %s", set.Namespace, set.Name, cords, err))
			return pod
		}
		nodeAffinity := organizationNodeAffinity(&coordinates[ordinal%len(coordinates)])
		if pod.Spec.Affinity == nil || pod.Spec.Affinity.NodeAffinity == nil {
			pod.Spec.Affinity = &v1.Affinity{NodeAffinity: nodeAffinity}
		}
	}
	return pod
}