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

     1  package statefulset
     2  
     3  import (
     4  	"context"
     5  
     6  	appsv1 "k8s.io/api/apps/v1"
     7  	corev1 "k8s.io/api/core/v1"
     8  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
     9  	"k8s.io/apimachinery/pkg/types"
    10  	"sigs.k8s.io/controller-runtime/pkg/client"
    11  
    12  	api "github.com/percona/percona-xtradb-cluster-operator/pkg/apis/pxc/v1"
    13  	app "github.com/percona/percona-xtradb-cluster-operator/pkg/pxc/app"
    14  	"github.com/percona/percona-xtradb-cluster-operator/pkg/pxc/users"
    15  	"github.com/pkg/errors"
    16  )
    17  
    18  const (
    19  	proxyName             = "proxysql"
    20  	proxyDataVolumeName   = "proxydata"
    21  	proxyConfigVolumeName = "config"
    22  )
    23  
    24  type Proxy struct {
    25  	sfs     *appsv1.StatefulSet
    26  	labels  map[string]string
    27  	service string
    28  }
    29  
    30  func NewProxy(cr *api.PerconaXtraDBCluster) *Proxy {
    31  	sfs := &appsv1.StatefulSet{
    32  		TypeMeta: metav1.TypeMeta{
    33  			APIVersion: "apps/v1",
    34  			Kind:       "StatefulSet",
    35  		},
    36  		ObjectMeta: metav1.ObjectMeta{
    37  			Name:      cr.Name + "-" + proxyName,
    38  			Namespace: cr.Namespace,
    39  		},
    40  	}
    41  
    42  	labels := map[string]string{
    43  		"app.kubernetes.io/name":       "percona-xtradb-cluster",
    44  		"app.kubernetes.io/instance":   cr.Name,
    45  		"app.kubernetes.io/component":  proxyName,
    46  		"app.kubernetes.io/managed-by": "percona-xtradb-cluster-operator",
    47  		"app.kubernetes.io/part-of":    "percona-xtradb-cluster",
    48  	}
    49  
    50  	return &Proxy{
    51  		sfs:     sfs,
    52  		labels:  labels,
    53  		service: cr.Name + "-proxysql-unready",
    54  	}
    55  }
    56  
    57  func (c *Proxy) Name() string {
    58  	return proxyName
    59  }
    60  
    61  func (c *Proxy) AppContainer(spec *api.PodSpec, secrets string, cr *api.PerconaXtraDBCluster,
    62  	availableVolumes []corev1.Volume,
    63  ) (corev1.Container, error) {
    64  	appc := corev1.Container{
    65  		Name:            proxyName,
    66  		Image:           spec.Image,
    67  		ImagePullPolicy: spec.ImagePullPolicy,
    68  		Ports: []corev1.ContainerPort{
    69  			{
    70  				ContainerPort: 3306,
    71  				Name:          "mysql",
    72  			},
    73  			{
    74  				ContainerPort: 6032,
    75  				Name:          "proxyadm",
    76  			},
    77  		},
    78  		VolumeMounts: []corev1.VolumeMount{
    79  			{
    80  				Name:      proxyDataVolumeName,
    81  				MountPath: "/var/lib/proxysql",
    82  			},
    83  			{
    84  				Name:      "ssl",
    85  				MountPath: "/etc/proxysql/ssl",
    86  			},
    87  			{
    88  				Name:      "ssl-internal",
    89  				MountPath: "/etc/proxysql/ssl-internal",
    90  			},
    91  		},
    92  		Env: []corev1.EnvVar{
    93  			{
    94  				Name:  "PXC_SERVICE",
    95  				Value: c.labels["app.kubernetes.io/instance"] + "-pxc",
    96  			},
    97  			{
    98  				Name: "MYSQL_ROOT_PASSWORD",
    99  				ValueFrom: &corev1.EnvVarSource{
   100  					SecretKeyRef: app.SecretKeySelector(secrets, users.Root),
   101  				},
   102  			},
   103  			{
   104  				Name:  "PROXY_ADMIN_USER",
   105  				Value: users.ProxyAdmin,
   106  			},
   107  			{
   108  				Name: "PROXY_ADMIN_PASSWORD",
   109  				ValueFrom: &corev1.EnvVarSource{
   110  					SecretKeyRef: app.SecretKeySelector(secrets, users.ProxyAdmin),
   111  				},
   112  			},
   113  			{
   114  				Name: "MONITOR_PASSWORD",
   115  				ValueFrom: &corev1.EnvVarSource{
   116  					SecretKeyRef: app.SecretKeySelector(secrets, users.Monitor),
   117  				},
   118  			},
   119  		},
   120  		SecurityContext: spec.ContainerSecurityContext,
   121  		Resources:       spec.Resources,
   122  	}
   123  	if cr.CompareVersionWith("1.9.0") >= 0 {
   124  		fvar := true
   125  		appc.EnvFrom = []corev1.EnvFromSource{
   126  			{
   127  				SecretRef: &corev1.SecretEnvSource{
   128  					LocalObjectReference: corev1.LocalObjectReference{
   129  						Name: cr.Spec.ProxySQL.EnvVarsSecretName,
   130  					},
   131  					Optional: &fvar,
   132  				},
   133  			},
   134  		}
   135  
   136  	}
   137  
   138  	if api.ContainsVolume(availableVolumes, proxyConfigVolumeName) {
   139  		appc.VolumeMounts = append(appc.VolumeMounts, corev1.VolumeMount{
   140  			Name:      proxyConfigVolumeName,
   141  			MountPath: "/etc/proxysql/",
   142  		})
   143  	}
   144  
   145  	if cr.CompareVersionWith("1.5.0") >= 0 {
   146  		appc.Env[1] = corev1.EnvVar{
   147  			Name: "OPERATOR_PASSWORD",
   148  			ValueFrom: &corev1.EnvVarSource{
   149  				SecretKeyRef: app.SecretKeySelector(secrets, users.Operator),
   150  			},
   151  		}
   152  	}
   153  
   154  	if cr.CompareVersionWith("1.11.0") >= 0 && cr.Spec.ProxySQL != nil && cr.Spec.ProxySQL.HookScript != "" {
   155  		appc.VolumeMounts = append(appc.VolumeMounts, corev1.VolumeMount{
   156  			Name:      "hookscript",
   157  			MountPath: "/opt/percona/hookscript",
   158  		})
   159  	}
   160  
   161  	if cr.Spec.ProxySQL != nil && (cr.Spec.ProxySQL.Lifecycle.PostStart != nil || cr.Spec.ProxySQL.Lifecycle.PreStop != nil) {
   162  		appc.Lifecycle = &cr.Spec.ProxySQL.Lifecycle
   163  	}
   164  
   165  	return appc, nil
   166  }
   167  
   168  func (c *Proxy) SidecarContainers(spec *api.PodSpec, secrets string, cr *api.PerconaXtraDBCluster) ([]corev1.Container, error) {
   169  	pxcMonit := corev1.Container{
   170  		Name:            "pxc-monit",
   171  		Image:           spec.Image,
   172  		ImagePullPolicy: spec.ImagePullPolicy,
   173  		Args: []string{
   174  			"/usr/bin/peer-list",
   175  			"-on-change=/usr/bin/add_pxc_nodes.sh",
   176  			"-service=$(PXC_SERVICE)",
   177  		},
   178  		Resources: spec.SidecarResources,
   179  		Env: []corev1.EnvVar{
   180  			{
   181  				Name:  "PXC_SERVICE",
   182  				Value: c.labels["app.kubernetes.io/instance"] + "-pxc",
   183  			},
   184  			{
   185  				Name: "MYSQL_ROOT_PASSWORD",
   186  				ValueFrom: &corev1.EnvVarSource{
   187  					SecretKeyRef: app.SecretKeySelector(secrets, users.Root),
   188  				},
   189  			},
   190  			{
   191  				Name:  "PROXY_ADMIN_USER",
   192  				Value: users.ProxyAdmin,
   193  			},
   194  			{
   195  				Name: "PROXY_ADMIN_PASSWORD",
   196  				ValueFrom: &corev1.EnvVarSource{
   197  					SecretKeyRef: app.SecretKeySelector(secrets, users.ProxyAdmin),
   198  				},
   199  			},
   200  			{
   201  				Name: "MONITOR_PASSWORD",
   202  				ValueFrom: &corev1.EnvVarSource{
   203  					SecretKeyRef: app.SecretKeySelector(secrets, users.Monitor),
   204  				},
   205  			},
   206  		},
   207  	}
   208  
   209  	proxysqlMonit := corev1.Container{
   210  		Name:            "proxysql-monit",
   211  		Image:           spec.Image,
   212  		ImagePullPolicy: spec.ImagePullPolicy,
   213  		Args: []string{
   214  			"/usr/bin/peer-list",
   215  			"-on-change=/usr/bin/add_proxysql_nodes.sh",
   216  			"-service=$(PROXYSQL_SERVICE)",
   217  		},
   218  		Resources: spec.SidecarResources,
   219  		Env: []corev1.EnvVar{
   220  			{
   221  				Name:  "PROXYSQL_SERVICE",
   222  				Value: c.labels["app.kubernetes.io/instance"] + "-proxysql-unready",
   223  			},
   224  			{
   225  				Name: "MYSQL_ROOT_PASSWORD",
   226  				ValueFrom: &corev1.EnvVarSource{
   227  					SecretKeyRef: app.SecretKeySelector(secrets, users.Root),
   228  				},
   229  			},
   230  			{
   231  				Name:  "PROXY_ADMIN_USER",
   232  				Value: users.ProxyAdmin,
   233  			},
   234  			{
   235  				Name: "PROXY_ADMIN_PASSWORD",
   236  				ValueFrom: &corev1.EnvVarSource{
   237  					SecretKeyRef: app.SecretKeySelector(secrets, users.ProxyAdmin),
   238  				},
   239  			},
   240  			{
   241  				Name: "MONITOR_PASSWORD",
   242  				ValueFrom: &corev1.EnvVarSource{
   243  					SecretKeyRef: app.SecretKeySelector(secrets, users.Monitor),
   244  				},
   245  			},
   246  		},
   247  	}
   248  	if cr.Spec.AllowUnsafeConfig && (cr.Spec.TLS == nil || cr.Spec.TLS.IssuerConf == nil) {
   249  		pxcMonit.Env = append(pxcMonit.Env, corev1.EnvVar{
   250  			Name:  "SSL_DIR",
   251  			Value: "/dev/null",
   252  		})
   253  		proxysqlMonit.Env = append(proxysqlMonit.Env, corev1.EnvVar{
   254  			Name:  "SSL_DIR",
   255  			Value: "/dev/null",
   256  		})
   257  	}
   258  	if cr.CompareVersionWith("1.9.0") >= 0 {
   259  		fvar := true
   260  		envFrom := corev1.EnvFromSource{
   261  			SecretRef: &corev1.SecretEnvSource{
   262  				LocalObjectReference: corev1.LocalObjectReference{
   263  					Name: cr.Spec.ProxySQL.EnvVarsSecretName,
   264  				},
   265  				Optional: &fvar,
   266  			},
   267  		}
   268  		pxcMonit.EnvFrom = append(pxcMonit.EnvFrom, envFrom)
   269  		proxysqlMonit.EnvFrom = append(proxysqlMonit.EnvFrom, envFrom)
   270  	}
   271  	if cr.CompareVersionWith("1.5.0") >= 0 {
   272  		operEnv := corev1.EnvVar{
   273  			Name: "OPERATOR_PASSWORD",
   274  			ValueFrom: &corev1.EnvVarSource{
   275  				SecretKeyRef: app.SecretKeySelector(secrets, users.Operator),
   276  			},
   277  		}
   278  		pxcMonit.Env[1] = operEnv
   279  		proxysqlMonit.Env[1] = operEnv
   280  	}
   281  
   282  	return []corev1.Container{pxcMonit, proxysqlMonit}, nil
   283  }
   284  
   285  func (c *Proxy) LogCollectorContainer(_ *api.LogCollectorSpec, _ string, _ string, _ *api.PerconaXtraDBCluster) ([]corev1.Container, error) {
   286  	return nil, nil
   287  }
   288  
   289  func (c *Proxy) PMMContainer(ctx context.Context, cl client.Client, spec *api.PMMSpec, secret *corev1.Secret, cr *api.PerconaXtraDBCluster) (*corev1.Container, error) {
   290  	envVarsSecret := &corev1.Secret{}
   291  	err := cl.Get(ctx, types.NamespacedName{Name: cr.Spec.PXC.EnvVarsSecretName, Namespace: cr.Namespace}, envVarsSecret)
   292  	if client.IgnoreNotFound(err) != nil {
   293  		return nil, errors.Wrap(err, "get env vars secret")
   294  	}
   295  
   296  	ct := app.PMMClient(cr, spec, secret, envVarsSecret)
   297  
   298  	pmmEnvs := []corev1.EnvVar{
   299  		{
   300  			Name:  "DB_TYPE",
   301  			Value: "proxysql",
   302  		},
   303  		{
   304  			Name:  "MONITOR_USER",
   305  			Value: users.Monitor,
   306  		},
   307  		{
   308  			Name: "MONITOR_PASSWORD",
   309  			ValueFrom: &corev1.EnvVarSource{
   310  				SecretKeyRef: app.SecretKeySelector(secret.Name, users.Monitor),
   311  			},
   312  		},
   313  	}
   314  
   315  	dbEnvs := []corev1.EnvVar{
   316  		{
   317  			Name:  "DB_USER",
   318  			Value: users.Monitor,
   319  		},
   320  		{
   321  			Name: "DB_PASSWORD",
   322  			ValueFrom: &corev1.EnvVarSource{
   323  				SecretKeyRef: app.SecretKeySelector(secret.Name, users.Monitor),
   324  			},
   325  		},
   326  		{
   327  			Name:  "DB_CLUSTER",
   328  			Value: app.Name,
   329  		},
   330  		{
   331  			Name:  "DB_HOST",
   332  			Value: "localhost",
   333  		},
   334  		{
   335  			Name:  "DB_PORT",
   336  			Value: "6032",
   337  		},
   338  	}
   339  
   340  	dbArgsEnv := []corev1.EnvVar{
   341  		{
   342  			Name:  "DB_ARGS",
   343  			Value: "--dsn $(MONITOR_USER):$(MONITOR_PASSWORD)@tcp(localhost:6032)/",
   344  		},
   345  	}
   346  
   347  	ct.Env = append(ct.Env, pmmEnvs...)
   348  	if cr.CompareVersionWith("1.2.0") >= 0 {
   349  		ct.Env = append(ct.Env, dbEnvs...)
   350  		ct.Resources = spec.Resources
   351  	} else {
   352  		ct.Env = append(ct.Env, dbArgsEnv...)
   353  	}
   354  
   355  	if cr.CompareVersionWith("1.9.0") >= 0 {
   356  		fvar := true
   357  		ct.EnvFrom = []corev1.EnvFromSource{
   358  			{
   359  				SecretRef: &corev1.SecretEnvSource{
   360  					LocalObjectReference: corev1.LocalObjectReference{
   361  						Name: cr.Spec.ProxySQL.EnvVarsSecretName,
   362  					},
   363  					Optional: &fvar,
   364  				},
   365  			},
   366  		}
   367  	}
   368  
   369  	if cr.CompareVersionWith("1.7.0") >= 0 {
   370  		PmmProxysqlParams := ""
   371  		if spec.ProxysqlParams != "" {
   372  			PmmProxysqlParams = spec.ProxysqlParams
   373  		}
   374  		clusterPmmEnvs := []corev1.EnvVar{
   375  			{
   376  				Name:  "CLUSTER_NAME",
   377  				Value: cr.Name,
   378  			},
   379  			{
   380  				Name:  "PMM_ADMIN_CUSTOM_PARAMS",
   381  				Value: PmmProxysqlParams,
   382  			},
   383  		}
   384  		ct.Env = append(ct.Env, clusterPmmEnvs...)
   385  		pmmAgentScriptEnv := app.PMMAgentScript(cr, "proxysql")
   386  		ct.Env = append(ct.Env, pmmAgentScriptEnv...)
   387  	}
   388  
   389  	if cr.CompareVersionWith("1.10.0") >= 0 {
   390  		// PMM team added these flags which allows us to avoid
   391  		// container crash, but just restart pmm-agent till it recovers
   392  		// the connection.
   393  		sidecarEnvs := []corev1.EnvVar{
   394  			{
   395  				Name:  "PMM_AGENT_SIDECAR",
   396  				Value: "true",
   397  			},
   398  			{
   399  				Name:  "PMM_AGENT_SIDECAR_SLEEP",
   400  				Value: "5",
   401  			},
   402  		}
   403  		ct.Env = append(ct.Env, sidecarEnvs...)
   404  	}
   405  
   406  	if cr.CompareVersionWith("1.14.0") >= 0 {
   407  		// PMM team moved temp directory to /usr/local/percona/pmm2/tmp
   408  		// but it doesn't work on OpenShift so we set it back to /tmp
   409  		sidecarEnvs := []corev1.EnvVar{
   410  			{
   411  				Name:  "PMM_AGENT_PATHS_TEMPDIR",
   412  				Value: "/tmp",
   413  			},
   414  		}
   415  		ct.Env = append(ct.Env, sidecarEnvs...)
   416  	}
   417  
   418  	return &ct, nil
   419  }
   420  
   421  func (c *Proxy) Volumes(podSpec *api.PodSpec, cr *api.PerconaXtraDBCluster, vg api.CustomVolumeGetter) (*api.Volume, error) {
   422  	ls := c.Labels()
   423  
   424  	vol := app.Volumes(podSpec, proxyDataVolumeName)
   425  	vol.Volumes = append(
   426  		vol.Volumes,
   427  		app.GetSecretVolumes("ssl-internal", podSpec.SSLInternalSecretName, true),
   428  		app.GetSecretVolumes("ssl", podSpec.SSLSecretName, cr.Spec.AllowUnsafeConfig),
   429  	)
   430  
   431  	configVolume, err := vg(cr.Namespace, proxyConfigVolumeName, ls["app.kubernetes.io/instance"]+"-proxysql", false)
   432  	if err != nil && !errors.Is(err, api.NoCustomVolumeErr) {
   433  		return nil, err
   434  	}
   435  	if err == nil {
   436  		vol.Volumes = append(vol.Volumes, configVolume)
   437  	}
   438  	if cr.CompareVersionWith("1.11.0") >= 0 && cr.Spec.ProxySQL != nil && cr.Spec.ProxySQL.HookScript != "" {
   439  		vol.Volumes = append(vol.Volumes,
   440  			app.GetConfigVolumes("hookscript", ls["app.kubernetes.io/instance"]+"-"+ls["app.kubernetes.io/component"]+"-hookscript"))
   441  	}
   442  	if cr.CompareVersionWith("1.13.0") >= 0 {
   443  		vol.Volumes = append(vol.Volumes,
   444  			corev1.Volume{
   445  				Name: app.BinVolumeName,
   446  				VolumeSource: corev1.VolumeSource{
   447  					EmptyDir: &corev1.EmptyDirVolumeSource{},
   448  				},
   449  			},
   450  		)
   451  	}
   452  	return vol, nil
   453  }
   454  
   455  func (c *Proxy) StatefulSet() *appsv1.StatefulSet {
   456  	return c.sfs
   457  }
   458  
   459  func (c *Proxy) Labels() map[string]string {
   460  	return c.labels
   461  }
   462  
   463  func (c *Proxy) Service() string {
   464  	return c.service
   465  }
   466  
   467  func (c *Proxy) UpdateStrategy(cr *api.PerconaXtraDBCluster) appsv1.StatefulSetUpdateStrategy {
   468  	switch cr.Spec.UpdateStrategy {
   469  	case appsv1.OnDeleteStatefulSetStrategyType:
   470  		return appsv1.StatefulSetUpdateStrategy{Type: appsv1.OnDeleteStatefulSetStrategyType}
   471  	case api.SmartUpdateStatefulSetStrategyType:
   472  		return appsv1.StatefulSetUpdateStrategy{Type: appsv1.RollingUpdateStatefulSetStrategyType}
   473  	default:
   474  		var zero int32 = 0
   475  		return appsv1.StatefulSetUpdateStrategy{
   476  			Type: appsv1.RollingUpdateStatefulSetStrategyType,
   477  			RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{
   478  				Partition: &zero,
   479  			},
   480  		}
   481  	}
   482  }