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 }