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

     1  package pxc
     2  
     3  import (
     4  	corev1 "k8s.io/api/core/v1"
     5  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
     6  	"k8s.io/apimachinery/pkg/util/intstr"
     7  
     8  	api "github.com/percona/percona-xtradb-cluster-operator/pkg/apis/pxc/v1"
     9  )
    10  
    11  const (
    12  	// HeadlessServiceAnnotation is the annotation key for headless service
    13  	HeadlessServiceAnnotation = "percona.com/headless-service"
    14  )
    15  
    16  func NewServicePXC(cr *api.PerconaXtraDBCluster) *corev1.Service {
    17  	obj := &corev1.Service{
    18  		TypeMeta: metav1.TypeMeta{
    19  			APIVersion: "v1",
    20  			Kind:       "Service",
    21  		},
    22  		ObjectMeta: metav1.ObjectMeta{
    23  			Name:      cr.Name + "-" + appName,
    24  			Namespace: cr.Namespace,
    25  			Labels: map[string]string{
    26  				"app.kubernetes.io/name":     "percona-xtradb-cluster",
    27  				"app.kubernetes.io/instance": cr.Name,
    28  			},
    29  		},
    30  		Spec: corev1.ServiceSpec{
    31  			Ports: []corev1.ServicePort{
    32  				{
    33  					Port: 3306,
    34  					Name: "mysql",
    35  				},
    36  			},
    37  			ClusterIP: "None",
    38  			Selector: map[string]string{
    39  				"app.kubernetes.io/name":      "percona-xtradb-cluster",
    40  				"app.kubernetes.io/instance":  cr.Name,
    41  				"app.kubernetes.io/component": appName,
    42  			},
    43  		},
    44  	}
    45  
    46  	if cr.CompareVersionWith("1.6.0") >= 0 {
    47  		obj.Spec.Ports = append(
    48  			obj.Spec.Ports,
    49  			corev1.ServicePort{
    50  				Port: 33062,
    51  				Name: "mysql-admin",
    52  			},
    53  		)
    54  	}
    55  
    56  	if cr.CompareVersionWith("1.9.0") >= 0 {
    57  		obj.ObjectMeta.Labels["app.kubernetes.io/component"] = appName
    58  		obj.ObjectMeta.Labels["app.kubernetes.io/managed-by"] = "percona-xtradb-cluster-operator"
    59  		obj.ObjectMeta.Labels["app.kubernetes.io/part-of"] = "percona-xtradb-cluster"
    60  
    61  		obj.Spec.Ports = append(
    62  			obj.Spec.Ports,
    63  			corev1.ServicePort{
    64  				Port: 33060,
    65  				Name: "mysqlx",
    66  			},
    67  		)
    68  	}
    69  
    70  	if cr.CompareVersionWith("1.14.0") >= 0 {
    71  		if cr.Spec.PXC != nil {
    72  			obj.Annotations = cr.Spec.PXC.Expose.Annotations
    73  			obj.Labels = fillServiceLabels(obj.Labels, cr.Spec.PXC.Expose.Labels)
    74  		}
    75  	}
    76  
    77  	return obj
    78  }
    79  
    80  func NewServicePXCUnready(cr *api.PerconaXtraDBCluster) *corev1.Service {
    81  	obj := &corev1.Service{
    82  		TypeMeta: metav1.TypeMeta{
    83  			APIVersion: "v1",
    84  			Kind:       "Service",
    85  		},
    86  		ObjectMeta: metav1.ObjectMeta{
    87  			Name:      cr.Name + "-" + appName + "-unready",
    88  			Namespace: cr.Namespace,
    89  			Annotations: map[string]string{
    90  				"service.alpha.kubernetes.io/tolerate-unready-endpoints": "true",
    91  			},
    92  			Labels: map[string]string{
    93  				"app.kubernetes.io/name":     "percona-xtradb-cluster",
    94  				"app.kubernetes.io/instance": cr.Name,
    95  			},
    96  		},
    97  		Spec: corev1.ServiceSpec{
    98  			Ports: []corev1.ServicePort{
    99  				{
   100  					Port: 3306,
   101  					Name: "mysql",
   102  				},
   103  			},
   104  			ClusterIP: "None",
   105  			Selector: map[string]string{
   106  				"app.kubernetes.io/name":      "percona-xtradb-cluster",
   107  				"app.kubernetes.io/instance":  cr.Name,
   108  				"app.kubernetes.io/component": appName,
   109  			},
   110  		},
   111  	}
   112  
   113  	if cr.CompareVersionWith("1.6.0") >= 0 {
   114  		obj.Spec.Ports = append(
   115  			obj.Spec.Ports,
   116  			corev1.ServicePort{
   117  				Port: 33062,
   118  				Name: "mysql-admin",
   119  			},
   120  		)
   121  	}
   122  
   123  	if cr.CompareVersionWith("1.9.0") >= 0 {
   124  		obj.ObjectMeta.Labels["app.kubernetes.io/component"] = appName
   125  		obj.ObjectMeta.Labels["app.kubernetes.io/managed-by"] = "percona-xtradb-cluster-operator"
   126  		obj.ObjectMeta.Labels["app.kubernetes.io/part-of"] = "percona-xtradb-cluster"
   127  
   128  		obj.Spec.Ports = append(
   129  			obj.Spec.Ports,
   130  			corev1.ServicePort{
   131  				Port: 33060,
   132  				Name: "mysqlx",
   133  			},
   134  		)
   135  	}
   136  
   137  	if cr.CompareVersionWith("1.10.0") >= 0 {
   138  		obj.Spec.PublishNotReadyAddresses = true
   139  		delete(obj.ObjectMeta.Annotations, "service.alpha.kubernetes.io/tolerate-unready-endpoints")
   140  	}
   141  
   142  	if cr.CompareVersionWith("1.14.0") >= 0 {
   143  		if cr.Spec.PXC != nil {
   144  			obj.Annotations = cr.Spec.PXC.Expose.Annotations
   145  			obj.Labels = fillServiceLabels(obj.Labels, cr.Spec.PXC.Expose.Labels)
   146  		}
   147  	}
   148  
   149  	return obj
   150  }
   151  
   152  func NewServiceProxySQLUnready(cr *api.PerconaXtraDBCluster) *corev1.Service {
   153  	obj := &corev1.Service{
   154  		TypeMeta: metav1.TypeMeta{
   155  			APIVersion: "v1",
   156  			Kind:       "Service",
   157  		},
   158  		ObjectMeta: metav1.ObjectMeta{
   159  			Name:      cr.ProxySQLUnreadyServiceNamespacedName().Name,
   160  			Namespace: cr.Namespace,
   161  			Annotations: map[string]string{
   162  				"service.alpha.kubernetes.io/tolerate-unready-endpoints": "true",
   163  			},
   164  			Labels: map[string]string{
   165  				"app.kubernetes.io/name":     "percona-xtradb-cluster",
   166  				"app.kubernetes.io/instance": cr.Name,
   167  			},
   168  		},
   169  		Spec: corev1.ServiceSpec{
   170  			Ports: []corev1.ServicePort{
   171  				{
   172  					Port: 3306,
   173  					Name: "mysql",
   174  				},
   175  				{
   176  					Port: 6032,
   177  					Name: "proxyadm",
   178  				},
   179  			},
   180  			ClusterIP: "None",
   181  			Selector: map[string]string{
   182  				"app.kubernetes.io/name":      "percona-xtradb-cluster",
   183  				"app.kubernetes.io/instance":  cr.Name,
   184  				"app.kubernetes.io/component": "proxysql",
   185  			},
   186  		},
   187  	}
   188  
   189  	if cr.CompareVersionWith("1.6.0") >= 0 {
   190  		obj.Spec.Ports = append(
   191  			obj.Spec.Ports,
   192  			corev1.ServicePort{
   193  				Port: 33062,
   194  				Name: "mysql-admin",
   195  			},
   196  		)
   197  	}
   198  
   199  	if cr.CompareVersionWith("1.9.0") >= 0 {
   200  		obj.ObjectMeta.Labels["app.kubernetes.io/component"] = "proxysql"
   201  		obj.ObjectMeta.Labels["app.kubernetes.io/managed-by"] = "percona-xtradb-cluster-operator"
   202  		obj.ObjectMeta.Labels["app.kubernetes.io/part-of"] = "percona-xtradb-cluster"
   203  	}
   204  
   205  	if cr.CompareVersionWith("1.10.0") >= 0 {
   206  		obj.Spec.PublishNotReadyAddresses = true
   207  		delete(obj.ObjectMeta.Annotations, "service.alpha.kubernetes.io/tolerate-unready-endpoints")
   208  	}
   209  
   210  	return obj
   211  }
   212  
   213  func NewServiceProxySQL(cr *api.PerconaXtraDBCluster) *corev1.Service {
   214  	svcType := corev1.ServiceTypeClusterIP
   215  
   216  	if cr.Spec.ProxySQL != nil {
   217  		if cr.CompareVersionWith("1.14.0") >= 0 && len(cr.Spec.ProxySQL.Expose.Type) > 0 {
   218  			svcType = cr.Spec.ProxySQL.Expose.Type
   219  		} else if len(cr.Spec.ProxySQL.ServiceType) > 0 {
   220  			svcType = cr.Spec.ProxySQL.ServiceType
   221  		}
   222  	}
   223  
   224  	serviceAnnotations := make(map[string]string)
   225  	serviceLabels := map[string]string{
   226  		"app.kubernetes.io/name":     "percona-xtradb-cluster",
   227  		"app.kubernetes.io/instance": cr.Name,
   228  	}
   229  	loadBalancerSourceRanges := []string{}
   230  	loadBalancerIP := ""
   231  
   232  	if cr.Spec.ProxySQL != nil {
   233  		if cr.CompareVersionWith("1.14.0") >= 0 {
   234  			serviceAnnotations = cr.Spec.ProxySQL.Expose.Annotations
   235  			serviceLabels = fillServiceLabels(serviceLabels, cr.Spec.ProxySQL.Expose.Labels)
   236  			loadBalancerSourceRanges = cr.Spec.ProxySQL.Expose.LoadBalancerSourceRanges
   237  			loadBalancerIP = cr.Spec.ProxySQL.Expose.LoadBalancerIP
   238  		} else {
   239  			serviceAnnotations = cr.Spec.ProxySQL.ServiceAnnotations
   240  			serviceLabels = fillServiceLabels(serviceLabels, cr.Spec.ProxySQL.ServiceLabels)
   241  			loadBalancerSourceRanges = cr.Spec.ProxySQL.LoadBalancerSourceRanges
   242  			loadBalancerIP = cr.Spec.ProxySQL.LoadBalancerIP
   243  		}
   244  	}
   245  
   246  	obj := &corev1.Service{
   247  		TypeMeta: metav1.TypeMeta{
   248  			APIVersion: "v1",
   249  			Kind:       "Service",
   250  		},
   251  		ObjectMeta: metav1.ObjectMeta{
   252  			Name:        cr.ProxySQLServiceNamespacedName().Name,
   253  			Namespace:   cr.Namespace,
   254  			Labels:      serviceLabels,
   255  			Annotations: serviceAnnotations,
   256  		},
   257  		Spec: corev1.ServiceSpec{
   258  			Type: svcType,
   259  			Ports: []corev1.ServicePort{
   260  				{
   261  					Port: 3306,
   262  					Name: "mysql",
   263  				},
   264  			},
   265  			Selector: map[string]string{
   266  				"app.kubernetes.io/name":      "percona-xtradb-cluster",
   267  				"app.kubernetes.io/instance":  cr.Name,
   268  				"app.kubernetes.io/component": "proxysql",
   269  			},
   270  			LoadBalancerSourceRanges: loadBalancerSourceRanges,
   271  			LoadBalancerIP:           loadBalancerIP,
   272  		},
   273  	}
   274  
   275  	if svcType == corev1.ServiceTypeLoadBalancer || svcType == corev1.ServiceTypeNodePort {
   276  		svcTrafficPolicyType := corev1.ServiceExternalTrafficPolicyTypeCluster
   277  
   278  		if cr.Spec.ProxySQL != nil {
   279  			if cr.CompareVersionWith("1.14.0") >= 0 && len(cr.Spec.ProxySQL.Expose.ExternalTrafficPolicy) > 0 {
   280  				svcTrafficPolicyType = cr.Spec.ProxySQL.Expose.ExternalTrafficPolicy
   281  			} else if len(cr.Spec.ProxySQL.ExternalTrafficPolicy) > 0 {
   282  				svcTrafficPolicyType = cr.Spec.ProxySQL.ExternalTrafficPolicy
   283  			}
   284  		}
   285  
   286  		obj.Spec.ExternalTrafficPolicy = svcTrafficPolicyType
   287  	}
   288  
   289  	if cr.Spec.ProxySQL != nil {
   290  		if cr.CompareVersionWith("1.14.0") >= 0 && cr.Spec.ProxySQL.Expose.Annotations != nil {
   291  			if cr.Spec.ProxySQL.Expose.Annotations[HeadlessServiceAnnotation] == "true" && svcType == corev1.ServiceTypeClusterIP {
   292  				obj.Annotations[HeadlessServiceAnnotation] = "true"
   293  				obj.Spec.ClusterIP = corev1.ClusterIPNone
   294  			}
   295  		} else if cr.Spec.ProxySQL.ServiceAnnotations != nil {
   296  			if cr.Spec.ProxySQL.ServiceAnnotations[HeadlessServiceAnnotation] == "true" && svcType == corev1.ServiceTypeClusterIP {
   297  				obj.Annotations[HeadlessServiceAnnotation] = "true"
   298  				obj.Spec.ClusterIP = corev1.ClusterIPNone
   299  			}
   300  		}
   301  	}
   302  
   303  	if cr.CompareVersionWith("1.6.0") >= 0 {
   304  		obj.Spec.Ports = append(
   305  			obj.Spec.Ports,
   306  			corev1.ServicePort{
   307  				Port: 33062,
   308  				Name: "mysql-admin",
   309  			},
   310  		)
   311  	}
   312  
   313  	if cr.CompareVersionWith("1.9.0") >= 0 {
   314  		obj.ObjectMeta.Labels["app.kubernetes.io/component"] = "proxysql"
   315  		obj.ObjectMeta.Labels["app.kubernetes.io/managed-by"] = "percona-xtradb-cluster-operator"
   316  		obj.ObjectMeta.Labels["app.kubernetes.io/part-of"] = "percona-xtradb-cluster"
   317  	}
   318  
   319  	return obj
   320  }
   321  
   322  func NewServiceHAProxy(cr *api.PerconaXtraDBCluster) *corev1.Service {
   323  	svcType := corev1.ServiceTypeClusterIP
   324  
   325  	if cr.Spec.HAProxy != nil {
   326  		if cr.CompareVersionWith("1.14.0") >= 0 && len(cr.Spec.HAProxy.ExposePrimary.Type) > 0 {
   327  			svcType = cr.Spec.HAProxy.ExposePrimary.Type
   328  		} else if len(cr.Spec.HAProxy.ServiceType) > 0 {
   329  			svcType = cr.Spec.HAProxy.ServiceType
   330  		}
   331  	}
   332  
   333  	serviceAnnotations := make(map[string]string)
   334  	serviceLabels := map[string]string{
   335  		"app.kubernetes.io/name":       "percona-xtradb-cluster",
   336  		"app.kubernetes.io/instance":   cr.Name,
   337  		"app.kubernetes.io/component":  "haproxy",
   338  		"app.kubernetes.io/managed-by": "percona-xtradb-cluster-operator",
   339  		"app.kubernetes.io/part-of":    "percona-xtradb-cluster",
   340  	}
   341  	loadBalancerSourceRanges := []string{}
   342  	loadBalancerIP := ""
   343  
   344  	if cr.Spec.HAProxy != nil {
   345  		if cr.CompareVersionWith("1.14.0") >= 0 {
   346  			serviceAnnotations = cr.Spec.HAProxy.ExposePrimary.Annotations
   347  			serviceLabels = fillServiceLabels(serviceLabels, cr.Spec.HAProxy.ExposePrimary.Labels)
   348  			loadBalancerSourceRanges = cr.Spec.HAProxy.ExposePrimary.LoadBalancerSourceRanges
   349  			loadBalancerIP = cr.Spec.HAProxy.ExposePrimary.LoadBalancerIP
   350  		} else {
   351  			serviceAnnotations = cr.Spec.HAProxy.ServiceAnnotations
   352  			serviceLabels = fillServiceLabels(serviceLabels, cr.Spec.HAProxy.PodSpec.ServiceLabels)
   353  			loadBalancerSourceRanges = cr.Spec.HAProxy.LoadBalancerSourceRanges
   354  			loadBalancerIP = cr.Spec.HAProxy.LoadBalancerIP
   355  		}
   356  	}
   357  
   358  	obj := &corev1.Service{
   359  		TypeMeta: metav1.TypeMeta{
   360  			APIVersion: "v1",
   361  			Kind:       "Service",
   362  		},
   363  		ObjectMeta: metav1.ObjectMeta{
   364  			Name:        cr.HaproxyServiceNamespacedName().Name,
   365  			Namespace:   cr.Namespace,
   366  			Labels:      serviceLabels,
   367  			Annotations: serviceAnnotations,
   368  		},
   369  		Spec: corev1.ServiceSpec{
   370  			Type: svcType,
   371  			Ports: []corev1.ServicePort{
   372  				{
   373  					Port:       3306,
   374  					TargetPort: intstr.FromInt(3306),
   375  					Name:       "mysql",
   376  				},
   377  				{
   378  					Port:       3309,
   379  					TargetPort: intstr.FromInt(3309),
   380  					Name:       "proxy-protocol",
   381  				},
   382  			},
   383  			Selector: map[string]string{
   384  				"app.kubernetes.io/name":      "percona-xtradb-cluster",
   385  				"app.kubernetes.io/instance":  cr.Name,
   386  				"app.kubernetes.io/component": "haproxy",
   387  			},
   388  			LoadBalancerSourceRanges: loadBalancerSourceRanges,
   389  			LoadBalancerIP:           loadBalancerIP,
   390  		},
   391  	}
   392  
   393  	if svcType == corev1.ServiceTypeLoadBalancer || svcType == corev1.ServiceTypeNodePort {
   394  		svcTrafficPolicyType := corev1.ServiceExternalTrafficPolicyTypeCluster
   395  
   396  		if cr.Spec.HAProxy != nil {
   397  			if cr.CompareVersionWith("1.14.0") >= 0 && len(cr.Spec.HAProxy.ExposePrimary.ExternalTrafficPolicy) > 0 {
   398  				svcTrafficPolicyType = cr.Spec.HAProxy.ExposePrimary.ExternalTrafficPolicy
   399  			} else if len(cr.Spec.HAProxy.ExternalTrafficPolicy) > 0 {
   400  				svcTrafficPolicyType = cr.Spec.HAProxy.ExternalTrafficPolicy
   401  			}
   402  		}
   403  
   404  		obj.Spec.ExternalTrafficPolicy = svcTrafficPolicyType
   405  	}
   406  
   407  	if cr.CompareVersionWith("1.6.0") >= 0 {
   408  		obj.Spec.Ports = append(
   409  			obj.Spec.Ports,
   410  			corev1.ServicePort{
   411  				Port:       33062,
   412  				TargetPort: intstr.FromInt(33062),
   413  				Name:       "mysql-admin",
   414  			},
   415  		)
   416  	}
   417  
   418  	if cr.CompareVersionWith("1.9.0") >= 0 {
   419  		obj.Spec.Ports = append(
   420  			obj.Spec.Ports,
   421  			corev1.ServicePort{
   422  				Port:       33060,
   423  				TargetPort: intstr.FromInt(33060),
   424  				Name:       "mysqlx",
   425  			},
   426  		)
   427  	}
   428  
   429  	if cr.Spec.HAProxy != nil {
   430  		if cr.CompareVersionWith("1.14.0") >= 0 {
   431  			if cr.Spec.HAProxy.ExposePrimary.Annotations != nil {
   432  				if cr.Spec.HAProxy.ExposePrimary.Annotations[HeadlessServiceAnnotation] == "true" && svcType == corev1.ServiceTypeClusterIP {
   433  					obj.Annotations[HeadlessServiceAnnotation] = "true"
   434  					obj.Spec.ClusterIP = corev1.ClusterIPNone
   435  				}
   436  			}
   437  		} else {
   438  			if cr.Spec.HAProxy.ServiceAnnotations != nil {
   439  				if cr.Spec.HAProxy.ServiceAnnotations[HeadlessServiceAnnotation] == "true" && svcType == corev1.ServiceTypeClusterIP {
   440  					obj.Annotations[HeadlessServiceAnnotation] = "true"
   441  					obj.Spec.ClusterIP = corev1.ClusterIPNone
   442  				}
   443  			}
   444  		}
   445  	}
   446  
   447  	return obj
   448  }
   449  
   450  func NewServiceHAProxyReplicas(cr *api.PerconaXtraDBCluster) *corev1.Service {
   451  	if cr.CompareVersionWith("1.14.0") >= 0 && cr.Spec.HAProxy != nil {
   452  		if cr.Spec.HAProxy.ExposeReplicas == nil {
   453  			cr.Spec.HAProxy.ExposeReplicas = &api.ServiceExpose{
   454  				Enabled: true,
   455  			}
   456  		}
   457  	}
   458  
   459  	svcType := corev1.ServiceTypeClusterIP
   460  	if cr.Spec.HAProxy != nil {
   461  		if cr.CompareVersionWith("1.14.0") >= 0 && len(cr.Spec.HAProxy.ExposeReplicas.Type) > 0 {
   462  			svcType = cr.Spec.HAProxy.ExposeReplicas.Type
   463  		} else if len(cr.Spec.HAProxy.ReplicasServiceType) > 0 {
   464  			svcType = cr.Spec.HAProxy.ReplicasServiceType
   465  		}
   466  	}
   467  
   468  	serviceAnnotations := make(map[string]string)
   469  	serviceLabels := map[string]string{
   470  		"app.kubernetes.io/name":       "percona-xtradb-cluster",
   471  		"app.kubernetes.io/instance":   cr.Name,
   472  		"app.kubernetes.io/component":  "haproxy",
   473  		"app.kubernetes.io/managed-by": "percona-xtradb-cluster-operator",
   474  		"app.kubernetes.io/part-of":    "percona-xtradb-cluster",
   475  	}
   476  	loadBalancerSourceRanges := []string{}
   477  	loadBalancerIP := ""
   478  	if cr.Spec.HAProxy != nil {
   479  		if cr.CompareVersionWith("1.14.0") >= 0 {
   480  			serviceAnnotations = cr.Spec.HAProxy.ExposeReplicas.Annotations
   481  			serviceLabels = fillServiceLabels(serviceLabels, cr.Spec.HAProxy.ExposeReplicas.Labels)
   482  		} else if cr.CompareVersionWith("1.12.0") >= 0 {
   483  			serviceAnnotations = cr.Spec.HAProxy.ReplicasServiceAnnotations
   484  			serviceLabels = fillServiceLabels(serviceLabels, cr.Spec.HAProxy.PodSpec.ReplicasServiceLabels)
   485  		} else {
   486  			serviceAnnotations = cr.Spec.HAProxy.ServiceAnnotations
   487  			serviceLabels = fillServiceLabels(serviceLabels, cr.Spec.HAProxy.PodSpec.ServiceLabels)
   488  		}
   489  
   490  		if cr.CompareVersionWith("1.14.0") >= 0 {
   491  			if cr.Spec.HAProxy.ExposeReplicas.LoadBalancerSourceRanges != nil {
   492  				loadBalancerSourceRanges = cr.Spec.HAProxy.ExposeReplicas.LoadBalancerSourceRanges
   493  			} else {
   494  				loadBalancerSourceRanges = cr.Spec.HAProxy.ExposePrimary.LoadBalancerSourceRanges
   495  			}
   496  			loadBalancerIP = cr.Spec.HAProxy.ExposeReplicas.LoadBalancerIP
   497  		} else {
   498  			if cr.Spec.HAProxy.ReplicasLoadBalancerSourceRanges != nil {
   499  				loadBalancerSourceRanges = cr.Spec.HAProxy.ReplicasLoadBalancerSourceRanges
   500  			} else {
   501  				loadBalancerSourceRanges = cr.Spec.HAProxy.LoadBalancerSourceRanges
   502  			}
   503  			loadBalancerIP = cr.Spec.HAProxy.ReplicasLoadBalancerIP
   504  		}
   505  	}
   506  
   507  	obj := &corev1.Service{
   508  		TypeMeta: metav1.TypeMeta{
   509  			APIVersion: "v1",
   510  			Kind:       "Service",
   511  		},
   512  		ObjectMeta: metav1.ObjectMeta{
   513  			Name:        cr.HAProxyReplicasNamespacedName().Name,
   514  			Namespace:   cr.Namespace,
   515  			Labels:      serviceLabels,
   516  			Annotations: serviceAnnotations,
   517  		},
   518  		Spec: corev1.ServiceSpec{
   519  			Type: svcType,
   520  			Ports: []corev1.ServicePort{
   521  				{
   522  					Port:       3306,
   523  					TargetPort: intstr.FromInt(3307),
   524  					Name:       "mysql-replicas",
   525  				},
   526  			},
   527  			Selector: map[string]string{
   528  				"app.kubernetes.io/name":      "percona-xtradb-cluster",
   529  				"app.kubernetes.io/instance":  cr.Name,
   530  				"app.kubernetes.io/component": "haproxy",
   531  			},
   532  			LoadBalancerSourceRanges: loadBalancerSourceRanges,
   533  			LoadBalancerIP:           loadBalancerIP,
   534  		},
   535  	}
   536  
   537  	if svcType == corev1.ServiceTypeLoadBalancer || svcType == corev1.ServiceTypeNodePort {
   538  		svcTrafficPolicyType := corev1.ServiceExternalTrafficPolicyTypeCluster
   539  
   540  		if cr.Spec.HAProxy != nil {
   541  			if cr.CompareVersionWith("1.14.0") >= 0 && len(cr.Spec.HAProxy.ExposeReplicas.ExternalTrafficPolicy) > 0 {
   542  				svcTrafficPolicyType = cr.Spec.HAProxy.ExposeReplicas.ExternalTrafficPolicy
   543  			} else if len(cr.Spec.HAProxy.ReplicasExternalTrafficPolicy) > 0 {
   544  				svcTrafficPolicyType = cr.Spec.HAProxy.ReplicasExternalTrafficPolicy
   545  			}
   546  		}
   547  
   548  		obj.Spec.ExternalTrafficPolicy = svcTrafficPolicyType
   549  	}
   550  
   551  	if cr.Spec.HAProxy != nil {
   552  		if cr.CompareVersionWith("1.14.0") >= 0 && cr.Spec.HAProxy.ExposeReplicas.Annotations != nil {
   553  			if cr.Spec.HAProxy.ExposeReplicas.Annotations[HeadlessServiceAnnotation] == "true" && svcType == corev1.ServiceTypeClusterIP {
   554  				obj.Annotations[HeadlessServiceAnnotation] = "true"
   555  				obj.Spec.ClusterIP = corev1.ClusterIPNone
   556  			}
   557  		} else if cr.Spec.HAProxy.ReplicasServiceAnnotations != nil {
   558  			if cr.Spec.HAProxy.ReplicasServiceAnnotations[HeadlessServiceAnnotation] == "true" && svcType == corev1.ServiceTypeClusterIP {
   559  				obj.Annotations[HeadlessServiceAnnotation] = "true"
   560  				obj.Spec.ClusterIP = corev1.ClusterIPNone
   561  			}
   562  		}
   563  	}
   564  
   565  	return obj
   566  }
   567  
   568  func fillServiceLabels(labels map[string]string, serviceLabels map[string]string) map[string]string {
   569  	for k, v := range serviceLabels {
   570  		if _, ok := labels[k]; ok {
   571  			continue
   572  		}
   573  		labels[k] = v
   574  	}
   575  	return labels
   576  }