github.com/percona/percona-xtradb-cluster-operator@v1.14.0/pkg/pxc/statefulset.go (about)

     1  package pxc
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"github.com/go-logr/logr"
     9  	"github.com/pkg/errors"
    10  	appsv1 "k8s.io/api/apps/v1"
    11  	corev1 "k8s.io/api/core/v1"
    12  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    13  	"sigs.k8s.io/controller-runtime/pkg/client"
    14  
    15  	api "github.com/percona/percona-xtradb-cluster-operator/pkg/apis/pxc/v1"
    16  )
    17  
    18  // StatefulSet returns StatefulSet according for app to podSpec
    19  func StatefulSet(ctx context.Context, cl client.Client, sfs api.StatefulApp, podSpec *api.PodSpec, cr *api.PerconaXtraDBCluster, secret *corev1.Secret,
    20  	initContainers []corev1.Container, log logr.Logger, vg api.CustomVolumeGetter,
    21  ) (*appsv1.StatefulSet, error) {
    22  	pod := corev1.PodSpec{
    23  		SecurityContext:               podSpec.PodSecurityContext,
    24  		NodeSelector:                  podSpec.NodeSelector,
    25  		Tolerations:                   podSpec.Tolerations,
    26  		SchedulerName:                 podSpec.SchedulerName,
    27  		PriorityClassName:             podSpec.PriorityClassName,
    28  		ImagePullSecrets:              podSpec.ImagePullSecrets,
    29  		TerminationGracePeriodSeconds: podSpec.TerminationGracePeriodSeconds,
    30  		RuntimeClassName:              podSpec.RuntimeClassName,
    31  	}
    32  	if cr.CompareVersionWith("1.5.0") >= 0 {
    33  		pod.ServiceAccountName = podSpec.ServiceAccountName
    34  	}
    35  	secrets := secret.Name
    36  	pod.Affinity = PodAffinity(podSpec.Affinity, sfs)
    37  	pod.TopologySpreadConstraints = PodTopologySpreadConstraints(podSpec.TopologySpreadConstraints, sfs.Labels())
    38  
    39  	if sfs.Labels()["app.kubernetes.io/component"] == "haproxy" && cr.CompareVersionWith("1.7.0") == -1 {
    40  		t := true
    41  		pod.ShareProcessNamespace = &t
    42  	}
    43  
    44  	sfsVolume, err := sfs.Volumes(podSpec, cr, vg)
    45  	if err != nil {
    46  		return nil, fmt.Errorf("failed to get volumes %v", err)
    47  	}
    48  
    49  	if sfsVolume != nil && sfsVolume.Volumes != nil {
    50  		pod.Volumes = sfsVolume.Volumes
    51  	}
    52  
    53  	appC, err := sfs.AppContainer(podSpec, secrets, cr, pod.Volumes)
    54  	if err != nil {
    55  		return nil, errors.Wrap(err, "app container")
    56  	}
    57  
    58  	if cr.Spec.PMM != nil && cr.Spec.PMM.Enabled {
    59  		if !cr.Spec.PMM.HasSecret(secret) {
    60  			log.Info(`Can't enable PMM: either "pmmserverkey" key doesn't exist in the secrets, or secrets and internal secrets are out of sync`,
    61  				"secrets", cr.Spec.SecretsName, "internalSecrets", "internal-"+cr.Name)
    62  		} else {
    63  			pmmC, err := sfs.PMMContainer(ctx, cl, cr.Spec.PMM, secret, cr)
    64  			if err != nil {
    65  				return nil, errors.Wrap(err, "pmm container error")
    66  			}
    67  			if pmmC != nil {
    68  				pod.Containers = append(pod.Containers, *pmmC)
    69  			}
    70  		}
    71  	}
    72  
    73  	if cr.Spec.LogCollector != nil && cr.Spec.LogCollector.Enabled && cr.CompareVersionWith("1.7.0") >= 0 {
    74  		logCollectorC, err := sfs.LogCollectorContainer(cr.Spec.LogCollector, cr.Spec.LogCollectorSecretName, secrets, cr)
    75  		if err != nil {
    76  			return nil, fmt.Errorf("logcollector container error: %v", err)
    77  		}
    78  		if logCollectorC != nil {
    79  			pod.Containers = append(pod.Containers, logCollectorC...)
    80  		}
    81  	}
    82  
    83  	if len(initContainers) > 0 {
    84  		pod.InitContainers = append(pod.InitContainers, initContainers...)
    85  	}
    86  
    87  	if podSpec.ForceUnsafeBootstrap && cr.CompareVersionWith("1.10.0") < 0 {
    88  		ic := appC.DeepCopy()
    89  		ic.Name = ic.Name + "-init-unsafe"
    90  		ic.Resources = podSpec.Resources
    91  		ic.ReadinessProbe = nil
    92  		ic.LivenessProbe = nil
    93  		ic.Command = []string{"/var/lib/mysql/unsafe-bootstrap.sh"}
    94  		pod.InitContainers = append(pod.InitContainers, *ic)
    95  	}
    96  
    97  	sideC, err := sfs.SidecarContainers(podSpec, secrets, cr)
    98  	if err != nil {
    99  		return nil, errors.Wrap(err, "sidecar container")
   100  	}
   101  	pod.Containers = append(pod.Containers, appC)
   102  	pod.Containers = append(pod.Containers, sideC...)
   103  	pod.Containers = api.AddSidecarContainers(log, pod.Containers, podSpec.Sidecars)
   104  	pod.Volumes = api.AddSidecarVolumes(log, pod.Volumes, podSpec.SidecarVolumes)
   105  
   106  	ls := sfs.Labels()
   107  
   108  	customLabels := make(map[string]string, len(ls))
   109  	for k, v := range ls {
   110  		customLabels[k] = v
   111  	}
   112  
   113  	for k, v := range podSpec.Labels {
   114  		if _, ok := customLabels[k]; !ok {
   115  			customLabels[k] = v
   116  		}
   117  	}
   118  
   119  	obj := sfs.StatefulSet()
   120  	obj.Spec = appsv1.StatefulSetSpec{
   121  		Replicas: &podSpec.Size,
   122  		Selector: &metav1.LabelSelector{
   123  			MatchLabels: ls,
   124  		},
   125  		ServiceName: sfs.Service(),
   126  		Template: corev1.PodTemplateSpec{
   127  			ObjectMeta: metav1.ObjectMeta{
   128  				Labels:      customLabels,
   129  				Annotations: podSpec.Annotations,
   130  			},
   131  			Spec: pod,
   132  		},
   133  		UpdateStrategy: sfs.UpdateStrategy(cr),
   134  	}
   135  
   136  	if sfsVolume != nil && sfsVolume.PVCs != nil {
   137  		obj.Spec.VolumeClaimTemplates = sfsVolume.PVCs
   138  	}
   139  	obj.Spec.VolumeClaimTemplates = api.AddSidecarPVCs(log, obj.Spec.VolumeClaimTemplates, podSpec.SidecarPVCs)
   140  
   141  	return obj, nil
   142  }
   143  
   144  // PodAffinity returns podAffinity options for the pod
   145  func PodAffinity(af *api.PodAffinity, app api.App) *corev1.Affinity {
   146  	if af == nil {
   147  		return nil
   148  	}
   149  
   150  	switch {
   151  	case af.Advanced != nil:
   152  		return af.Advanced
   153  	case af.TopologyKey != nil:
   154  		if strings.ToLower(*af.TopologyKey) == api.AffinityTopologyKeyOff {
   155  			return nil
   156  		}
   157  		return &corev1.Affinity{
   158  			PodAntiAffinity: &corev1.PodAntiAffinity{
   159  				RequiredDuringSchedulingIgnoredDuringExecution: []corev1.PodAffinityTerm{
   160  					{
   161  						LabelSelector: &metav1.LabelSelector{
   162  							MatchLabels: app.Labels(),
   163  						},
   164  						TopologyKey: *af.TopologyKey,
   165  					},
   166  				},
   167  			},
   168  		}
   169  	}
   170  
   171  	return nil
   172  }
   173  
   174  func PodTopologySpreadConstraints(tscs []corev1.TopologySpreadConstraint, ls map[string]string) []corev1.TopologySpreadConstraint {
   175  	result := make([]corev1.TopologySpreadConstraint, 0, len(tscs))
   176  
   177  	for _, tsc := range tscs {
   178  		if tsc.LabelSelector == nil && tsc.MatchLabelKeys == nil && len(ls) > 0 {
   179  			tsc.LabelSelector = &metav1.LabelSelector{
   180  				MatchLabels: ls,
   181  			}
   182  		}
   183  		if tsc.MaxSkew == 0 {
   184  			tsc.MaxSkew = 1
   185  		}
   186  		if tsc.TopologyKey == "" {
   187  			tsc.TopologyKey = api.DefaultAffinityTopologyKey
   188  		}
   189  		if tsc.WhenUnsatisfiable == "" {
   190  			tsc.WhenUnsatisfiable = corev1.ScheduleAnyway
   191  		}
   192  
   193  		result = append(result, tsc)
   194  	}
   195  	return result
   196  }
   197  
   198  func MergeTemplateAnnotations(sfs *appsv1.StatefulSet, annotations map[string]string) {
   199  	if len(annotations) == 0 {
   200  		return
   201  	}
   202  	if sfs.Spec.Template.Annotations == nil {
   203  		sfs.Spec.Template.Annotations = make(map[string]string)
   204  	}
   205  	for k, v := range annotations {
   206  		sfs.Spec.Template.Annotations[k] = v
   207  	}
   208  }